Well, just got some problems in JavaScript loop with http request involved. Take a short note showing how to deal with them which got ideas from the post: JavaScript: async/await with forEach().
face the issue
Okay, let’s try the example with axios
calls in a loop:
const axios = require('./node_modules/axios');
const getTitle = (num) => {
return new Promise((resolve, reject) => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${num}`)
.then(response => {
return resolve(response.data.title)
})
.catch(error => {
return reject(error.message)
})
})
}
[1, 2, 3, 4, 5].forEach(async (num) => {
await getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
})
console.log('Done')
Run this script with Node.js, you might see this:
Done
num 2: qui est esse
num 3: ea molestias quasi exercitationem repellat qui ipsa sit aut
num 1: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
num 4: eum et est occaecati
num 5: nesciunt quas odio
The result is the same for a simple for
loop:
for (let num of [1, 2, 3, 4, 5]) {
getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
}
Note the numbers were not in order.
In browsers with http request using fetch
API, it’s the same result as Promises returned.
let’s update
async ForEach
In the mentioned post above, we can re-write our own “async ForEach” function:
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
Run it again with:
asyncForEach([1, 2, 3, 4, 5], async (num) => {
await getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
})
console.log('Done')
You will get:
Done
num 1: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
num 2: qui est esse
num 3: ea molestias quasi exercitationem repellat qui ipsa sit aut
num 4: eum et est occaecati
num 5: nesciunt quas odio
Yeah, much better, but Done
shows early. That’s because the asyncForEach
is wrapped inside async
that returns a Promise
, however, we are not waiting for the Promise to be resolved. Okay, let’s move on to put all script inside the async
.
const start = async() => {
await asyncForEach([1, 2, 3, 4, 5], async (num) => {
await getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
})
console.log('Done')
}
start()
Well, you’ll get this result with the correct order:
num 1: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
num 2: qui est esse
num 3: ea molestias quasi exercitationem repellat qui ipsa sit aut
num 4: eum et est occaecati
num 5: nesciunt quas odio
Done
async for
As the asyncForEach
is actually using for
loop, why not just use for
loop that inside a async
to do the same job:
const start = async() => {
for (let num of [1, 2, 3, 4, 5]) {
await getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
}
console.log('Done')
}
start();
// ---
// num 1: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
// num 2: qui est esse
// num 3: ea molestias quasi exercitationem repellat qui ipsa sit aut
// num 4: eum et est occaecati
// num 5: nesciunt quas odio
// Done
The result is sequentially logged one by one after each Promise resolved.
async map
Also, with Promise.all()
you can get all results obtained and log them immediately:
const start = async () => {
await Promise.all([1, 2, 3, 4, 5].map(async num => {
await getTitle(num).then((title) => {
console.log(`num ${num}: ${title}`);
})
}))
console.log('Done')
}
start();
Much faster…
Thanks for all the examples online that make understanding easier… And, happy coding…