DEV Community

loading...

Javascripts async and loops

Norman
Programmer, OpenSource enthusiast and hacker based in Berlin, Germany.
・1 min read

Thanks to async/await many pieces of code became much more declarative. But there is one tricky scenario that I just bumped into: I needed to do a handful of API requests in a loop and refresh the list view afterwards. Easy peasy?

async updateAllItems (listOfItems) {
  listOfItems.forEach(async (item) => {
    await collection.updateItem(item.id, { status: 'coolNewStatus' })
  })
  const newItems = await collection.allItems()
  return newItems
}

What's the problem here? Some people might have seen it already (unlike me in the last couple of hours): newItems will be loaded before the loop ended because we're not actually waiting for it to end. Simply throwing another await in front of it doesn't cut it either.

There are at least three possible solutions, depending on your style.

✅ Sticking to the procedural approach:

async updateAllItems (listOfItems) {
  for (const item of listOfItems) {
    await collection.updateItem(item.id, { status: 'coolNewStatus' })
  }
  const newItems = await collection.allItems()
  return newItems
}

Why are people so afraid of for-loops anyway?

✅ Promise.all

async updateAllItems (listOfItems) {
  await Promise.all(listOfItems.map(item => {
    return collection.updateItem(item.id, { status: 'coolNewStatus' })
  }))
  const newItems = await collection.allItems()
  return newItems
}

A little lispy, isn't it?

❌ await async

async updateAllItems (listOfItems) {
  listOfItems.forEach(await (async (item) => {
    await collection.updateItem(item.id, { status: 'coolNewStatus' })
  }))
  const newItems = await collection.allItems()
  return newItems
}

This does NOT work. Why? No idea. Do you have any idea?

Anyways, I hope this helps you with your next async race condition. Cheers 🥂

Discussion (0)