Well, just got some problems in JavaScript loop with http request inside. Take a short note showing how to deal with it 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 in 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}`);
  })
}

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:

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()

Thanks for all the examples online that make understanding easier…