Handle Multiple Promises using Async/Await
Matthew Wang
Posted on May 7, 2022
In this example below, we loop through an Array of userIDs to fetch information about the user.
A naive approach may be to await each response one by one.
❌ The wrong and slow way
userIDs = ['fooID1', 'fooID2', ...]
const slowMethod = async (userIDs) => {
const results = [];
for (let i = 0; i < userIDs.length; i++) {
const res = await fetch(`https://api.foo.com/users?uid=${userIDs[i]}`)
const data = await res.json()
results.push(data);
}
return results;
}
await slowMethod(); //O(n) time
✔️ The Fast Way
userIDs = ['fooID1', 'fooID2', ...]
const fastCleanMethod = async (userIDs) => {
const promises = userIDs.map(async userID => {
const res = await fetch(`https://api.foo.com/users?uid=${userID}`);
return await res.json();
});
return await Promise.all(promises);
}
await fastCleanMethod(); //O(1) time
🧐 How it works
The fast method takes advantage of the Promise.all() method. The way it works is by fetching all the data first, then using Promise.all() to resolve/reject later.
By doing this, we do not have to wait for each response one by one.
Another Example of two separate fetches
Let's say we want to fetch user data and their entries. Here are two ways to do it.
❌ Fast but Fails Slow
const getUserData = async (userIDs) => {
const userDataPromise = fetch(`https://api.foo.com/users?uid=${userID}`);
const userEntriesPromise = fetch(`https://api.foo.com/entries?uid=${userID}`);
const [userDataRes, userEntriesRes] = [(await userDataPromise).json(), (await userEntriesPromise).json()];
const [userData, userEntries] = [await userDataRes, await userEntriesRes];
return {
...userData,
userEntries,
}
}
This method works well in most cases but if the second Promise is rejected bad things can happen. The reason it fails slow is because if the second Promise is rejected, the first Promise still runs for the whole duration.
Stack Overflow user @T.J. Crowder explains parallel Promise patterns best here.
✔️ Fast and Fails Fast
const getUserData = async (userIDs) => {
const userDataPromise = fetch(`https://api.foo.com/users?uid=${userID}`);
const userEntriesPromise = fetch(`https://api.foo.com/entries?uid=${userID}`);
const [userDataRes, userEntriesRes] = await Promise.all([userDataPromise, userEntriesPromise]);
const [userData, userEntries] = await Promise.all([userDataRes.json(), userEntriesRes.json()]);
return {
...userData,
userEntries,
}
}
We use Promise.all() because when a Promise is rejected, it immediately throws an error without waiting for other Promises.
It may be tempting to use pure async/await for handling multiple Promises but Promise.all() is still our best bet.
Posted on May 7, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
March 3, 2024