Recently wrote a static file server with a node when it came to a question, call problems await / async asynchronous functions in forEach loop inside. This problem also encountered a few times, write down here next time to avoid forgetting.
To reproduce the problem
var getNumbers = () => {
return Promise.resolve([1, 2, 3])
}
var multi = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (num) {
resolve(num * num)
} else {
reject(new Error('num not specified'))
}
}, 1000)
})
}
async function test () {
var nums = await getNumbers()
nums.forEach(async x => {
var res = await multi(x)
console.log(res)
})
}
test()
After the test function to perform my desired result is the code is executed serially, I'll be a second and so on each output a number ... 1 ... 4 ... 9, but the results obtained are now waiting for a with the second output 149
Find issues
Why in the for loop / for ... of which can be achieved await / async function, but it can not in forEach inside? First, we look at Polyfill MDN website
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
if (arguments.length > 1) {
T = thisArg;
}
k = 0;
while (k < len) {
var kValue;
if (k in O) {
kValue = O[k];
callback.call(T, kValue, k, O);
}
k++;
}
};
}
When we can see the forEach written inside the callback function will be called directly while circulating inside, under the simplified
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}
Equivalent in function for performing asynchronous loop
async function test () {
var nums = await getNumbers()
// nums.forEach(async x => {
// var res = await multi(x)
// console.log(res)
// })
for(let index = 0; index < nums.length; index++) {
(async x => {
var res = await multi(x)
console.log(res)
})(nums[index])
}
}
Solution
... for use in place of, or for loop forEach
async function test () {
var nums = await getNumbers()
for(let x of nums) {
var res = await multi(x)
console.log(res)
}
}