Incorrect Use of async
in forEach
Loops in JavaScript
Introduction
JavaScript provides the forEach
method to iterate over arrays, but when combined with async
/await
, it does not behave as expected. This leads to issues like unhandled promises, unexpected execution order, and asynchronous functions not waiting properly. Understanding why this happens and how to fix it is crucial for writing reliable asynchronous code.
1. Understanding forEach
and Asynchronous Code
What is forEach
?
forEach
is a higher-order function in JavaScript that allows iteration over an array. It takes a callback function as an argument and executes it for each array element.
const numbers = [1, 2, 3];
numbers.forEach(num => {
console.log(num);
});
Output:
1
2
3
forEach
executes synchronously.- It does not wait for asynchronous code inside the callback to complete.
2. Incorrect Use of async
Inside forEach
Many developers assume that using async
inside forEach
will make the loop wait for each iteration to complete before moving to the next. However, forEach
does not support await
properly.
Example of Incorrect Usage
const fetchData = async (num) => {
return new Promise(resolve => setTimeout(() => {
console.log(`Fetching data for ${num}`);
resolve();
}, 1000));
};
const numbers = [1, 2, 3];
numbers.forEach(async (num) => {
await fetchData(num);
console.log(`Finished processing ${num}`);
});
Expected Output (What we assume will happen)
Fetching data for 1
Finished processing 1
Fetching data for 2
Finished processing 2
Fetching data for 3
Finished processing 3
Actual Output (What happens instead)
Fetching data for 1
Fetching data for 2
Fetching data for 3
Finished processing 1
Finished processing 2
Finished processing 3
What Went Wrong?
forEach
does not wait for theasync
function to resolve.- The loop does not pause to let
await
complete before moving to the next iteration. - This results in all API calls happening in parallel instead of sequentially.
3. Correct Ways to Handle async
in Loops
Solution 1: Use a for...of
Loop
Instead of forEach
, use a for...of
loop, which properly supports await
.
const fetchData = async (num) => {
return new Promise(resolve => setTimeout(() => {
console.log(`Fetching data for ${num}`);
resolve();
}, 1000));
};
const processNumbers = async () => {
const numbers = [1, 2, 3];
for (const num of numbers) {
await fetchData(num);
console.log(`Finished processing ${num}`);
}
};
processNumbers();
Output (As Expected)
Fetching data for 1
Finished processing 1
Fetching data for 2
Finished processing 2
Fetching data for 3
Finished processing 3
- ✅
for...of
waits for eachawait
before moving to the next iteration.
Solution 2: Use map()
with Promise.all()
If you want parallel execution instead of sequential execution, use .map()
with Promise.all()
.
const fetchData = async (num) => {
return new Promise(resolve => setTimeout(() => {
console.log(`Fetching data for ${num}`);
resolve();
}, 1000));
};
const processNumbers = async () => {
const numbers = [1, 2, 3];
await Promise.all(numbers.map(async (num) => {
await fetchData(num);
console.log(`Finished processing ${num}`);
}));
};
processNumbers();
Output (Parallel Execution)
Fetching data for 1
Fetching data for 2
Fetching data for 3
Finished processing 1
Finished processing 2
Finished processing 3
- ✅
Promise.all()
ensures that all async operations are executed in parallel. - ✅ The function waits for all operations to complete before proceeding.
Solution 3: Using a Traditional for
Loop
A simple for
loop also supports await
correctly.
const numbers = [1, 2, 3];
async function processNumbers() {
for (let i = 0; i < numbers.length; i++) {
await fetchData(numbers[i]);
console.log(`Finished processing ${numbers[i]}`);
}
}
processNumbers();
- ✅ Works sequentially like
for...of
- ✅ Fully supports
await
4. Key Takeaways
❌ Avoid using async
inside forEach
because:
forEach
does not wait for async operations.- Execution order is not guaranteed.
- Can cause unexpected behavior and bugs.
✅ Use these alternatives instead:
for...of
→ Best for sequential execution.map()
+Promise.all()
→ Best for parallel execution.- Traditional
for
loop → Works likefor...of
.