JavaScript
async-await
axios
promises
asynchronous-programming

Why doesn't 'await' wait for axios request completes?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When await appears not to wait for an Axios request to complete, the issue is almost always that the calling function itself is not being awaited, or await is used inside a non-async callback (like forEach). The await keyword only pauses execution within the immediate async function scope — it does not block the outer caller unless that caller also awaits the returned promise. Understanding this scoping behavior is the key to debugging "await doesn't wait" problems.

The Problem

javascript
1// This function looks correct but the caller doesn't await it
2async function fetchData() {
3    const response = await axios.get('/api/users');
4    return response.data;
5}
6
7// BUG: fetchData() returns a Promise, not the resolved value
8const data = fetchData();
9console.log(data); // Promise { <pending> }

The await inside fetchData works correctly, but the caller does not await the function's returned promise. Since every async function returns a promise, you must await or .then() the result.

Fix 1: Await the Calling Function

javascript
1async function fetchData() {
2    const response = await axios.get('/api/users');
3    return response.data;
4}
5
6// The caller must also be async and use await
7async function main() {
8    const data = await fetchData();
9    console.log(data); // [{ id: 1, name: 'Alice' }, ...]
10}
11
12main();

Every function in the call chain between the await and the top-level entry point must either await the result or handle the returned promise with .then().

Fix 2: The forEach Trap

javascript
1// BUG: await inside forEach does NOT pause the outer function
2async function fetchAllUsers(ids) {
3    const users = [];
4    ids.forEach(async (id) => {
5        const response = await axios.get(`/api/users/${id}`);
6        users.push(response.data); // Runs after fetchAllUsers returns
7    });
8    return users; // Returns empty array
9}
10
11// FIX: Use for...of loop
12async function fetchAllUsers(ids) {
13    const users = [];
14    for (const id of ids) {
15        const response = await axios.get(`/api/users/${id}`);
16        users.push(response.data);
17    }
18    return users; // Returns all users (sequential)
19}
20
21// FIX (concurrent): Use Promise.all with map
22async function fetchAllUsersConcurrent(ids) {
23    const promises = ids.map(id => axios.get(`/api/users/${id}`));
24    const responses = await Promise.all(promises);
25    return responses.map(r => r.data);
26}

Array.forEach ignores the promise returned by the async callback. Use for...of for sequential execution or Promise.all with .map() for concurrent requests.

Fix 3: Missing Return in Intermediate Functions

javascript
1// BUG: missing return — the promise is created but not passed back
2function getUser(id) {
3    axios.get(`/api/users/${id}`); // No return!
4}
5
6async function main() {
7    const result = await getUser(1);
8    console.log(result); // undefined
9}
10
11// FIX: return the promise
12function getUser(id) {
13    return axios.get(`/api/users/${id}`);
14}
15
16async function main() {
17    const response = await getUser(1);
18    console.log(response.data); // { id: 1, name: 'Alice' }
19}

If a helper function creates a promise but doesn't return it, the caller has nothing to await.

Fix 4: Axios Interceptors and Error Handling

javascript
1// Axios interceptor that doesn't return the response
2axios.interceptors.response.use(
3    (response) => {
4        console.log('Response received');
5        // BUG: forgot to return response
6    }
7);
8
9// FIX: always return from interceptors
10axios.interceptors.response.use(
11    (response) => {
12        console.log('Response received');
13        return response; // Must return
14    },
15    (error) => {
16        console.error('Request failed:', error.message);
17        return Promise.reject(error); // Must reject
18    }
19);
20
21// Proper error handling with await
22async function fetchData() {
23    try {
24        const response = await axios.get('/api/users');
25        return response.data;
26    } catch (error) {
27        if (error.response) {
28            console.error('Server error:', error.response.status);
29        } else if (error.request) {
30            console.error('No response received');
31        } else {
32            console.error('Request setup error:', error.message);
33        }
34        throw error;
35    }
36}

Common Pitfalls

  • Not awaiting the async function itself: await only pauses the function it's inside. The caller must also await or .then() the returned promise, all the way up the call chain.
  • Using forEach with async callbacks: forEach ignores returned promises. Use for...of for sequential execution or Promise.all(arr.map(...)) for concurrent execution.
  • Forgetting to return the promise: If an intermediate function calls axios.get() without return, the caller awaits undefined instead of the actual response.
  • Interceptors swallowing responses: Axios interceptors must return the response (or Promise.reject(error) for errors). A missing return makes every request resolve to undefined.
  • Mixing .then() and await incorrectly: Avoid patterns like await axios.get(url).then(r => r.data) — while technically valid, they're confusing. Pick one style and use it consistently.

Summary

  • await only pauses the async function it appears in — callers must also await the result
  • forEach with async callbacks does not wait — use for...of or Promise.all with map
  • Always return promises from intermediate functions so callers can await them
  • Axios interceptors must return the response or reject the error
  • Wrap await calls in try/catch for proper error handling

Course illustration
Course illustration

All Rights Reserved.