Async Foreach
Read an article Exploring the JavaScript forEach Method for Looping Over Arrays by Jack Misteli today.
Jack announced the post on Coding Blocks Slack channel, and mentioned that,
I think most of you won't learn anything here it's pretty beginner stuff, hopefully there will be a nugget of new information.
He was right. I did get "a nugget of new information".
Wasn't aware of async foreach
method can work differently that I thought.
Setting await
outside Array#foreach
doesn't necessarily await/resolve promise inside it.
Reproducing the behavior
An await
on Array#foreach
would not resolve promise(s) inside it.
In the code snippet below, following output will print all at once after one second, NOT wait 1 second per number.
const waitFor = ms => new Promise(r => setTimeout(r, ms))
;[1, 2, 3].forEach(async n => {
await waitFor(1000)
console.log(`first number`, n)
})
result after 1 second (1000 milliseconds).
first number 1
first number 2
first number 3
Go ahead and click the play ▶ button to run the sample below.
But what if you want to wait for 1 second per each iteration?
Using asyncForEach method
I also googled and foudn this articled, JavaScript: async/await with forEach(), which dived deeper into the particular topic.
The author, Sébastien Chopin, created a wrapper method named asyncForEach
, which accepts an array, and a callbac, to operate on the array.
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
It reads like, given an array, await on callback of each array item.
The code below
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
const waitFor = ms => new Promise(r => setTimeout(r, ms))
async function main() {
asyncForEach([1, 2, 3], async n => {
await waitFor(1000)
console.log(`main 'asyncForEach' num=${n}`)
})
}
main()
results in following output, where each line is printed after one second.
main 'asyncForEach' num=1
main 'asyncForEach' num=2
main 'asyncForEach' num=3
Go ahead and click the play ▶ button to run the sample below.
Using "for await...of".
There is a new syntax in town, for await...of, which became avaiable in ECMAScript2018.
Let's say, you are mapping over an array, which does an async task.
Overly contrived for brevity
// You can use `fetch` logic here to get users or posts, etc. for `getN`.
const getN = async n => n
const waitFor = ms => new Promise(r => setTimeout(r, ms))
async function main() {
for await (const n of [1, 2, 3].map(getN)) {
await waitFor(1000)
console.log(`main 'for await of' num=${n}`)
}
}
main()
The code above will output each line every second, not at once.
main 'for await of' num=1
main 'for await of' num=2
main 'for await of' num=3
The behavior is the same as using asyncForEach
wrapper, just imperative.
Bonus
Jack extended Array
prototype as
Array.prototype.asyncForEach = async function(callback) {
let k = 0
while (k < this.length) {
if (!this[k]) return
let element = this[k]
// This will pause the execution of the code
await callback(element, k, this)
k += 1
}
}
and my friend, Nicolas Marcora helped me to understand the simplified implementation.
Array.prototype.asyncForEach = async function(callback) {
for (const index in this) {
const element = this[index]
// This will pause the execution of the code
await callback(element, index, this)
}
}
for...in
would return an index of the this
array, and you can get an element of each, which you can await on.
Image by PublicDomainPictures from Pixabay
Webmentions
Loading counts...