Async is one of the most important tool for Js developer. And since it’s flow is not procedural like normally seen in Js code, async code is a bit harder to follow. In this article, I will present a common case where loop and async function interact.
The situation
I have to make a loop through an array, then with each item make an async data call and do some calculation with the index inside the callback function. A similar situation can be found below:
var output = '';
var array = ['h','e','l','l','o'];// This is an example of an async function, like an AJAX callvar fetchData = function () {
return new Promise(function (resolve, reject) {
resolve();
});
};/*
Inside the loop below, we execute an async function and create
a string from each letter in the callback. - Expected output: hello
- Actual output: undefinedundefinedundefinedundefined
*/
for (var i = 0; i < array.length; i++) {
fetchData(array[i]).then(function () {
output += array[i];
});
}
We expect that as i increases, each item of the “hello” array will be added to the list. In reality though, we got a string of 4 undefined.
A simplified explanation
Below is the order the loop will be executed. You can see line output += array[i]; doesn’t run in normal top-to-bottom order, since it’s in a async callback


Because async callback runs when the promise is resolved, outside of the main execution order, when it runs, i is already equals array.length, as you can see in the chart. Therefore array[i] returns undefined :(
The underline reason: Variable scope
A variable’s scope defines the part of the program where the variable is accessible. For example, the classic case of variable scope is variable created inside a function
function sayHello() {
var hello = "Hello";
console.log(hello); // output: "Hello"
}console.log(hello); // output: undefined
An important notice here is that JavaScript traditionally does not support block level scoping, for example
for (var i = 0; i < 4; i++) {
// Do sth cool with i
}
console.log(i); // output: 4
Variable i is available outside of the loop block. The loop runs 4 times until i===4 where the condition fails. The same situation happens to our async loop above.
Solutions
The principle to solve this problem is to put the array[i] inside a scope, to prevent it from changing when value of i changes. There are 2 ways to do this
Save array[i] in a scoped variable by creating a function
As function variable is scoped, you can create a function wrap around the async call. This way, array[i] is saved, and you get the intended output.
function appendOutput (item) {
fetchData(item).then(function () {
output.innerHTML += item;
});
}for (var i = 0; i < array.length; i++) {
appendOutput(array[i]); // output: hello
}
Use ES6 :)
Fortunately, since ES6 (ES2015) you can use let to define a variable and it’s scoped in block level. So if your project is running with ES6, this will be the simple solution
for (let i = 0; i < array.length; i++) {
fetchData(array[i]).then(function () {
output.innerHTML += array[i]; // output: hello
});
}
Javascript is deceptively simple, but there’s a lower level of understanding (such as scope and memory) that when acquires, can help solve many “random” bugs. With this short article, I hope you has a better understanding of async and relieve some headaches of your own.