In javascript we often need to do multiple asynchronous things.
I'd like to use this post to show a few examples of doing things serially, and in parallel with promises.
Example 1: "Wait a second" x 3
First example, lets define a function where we "wait a second", three times in a row.
This function will be called serial.
After that, we'll call the "wait a second" function, three times in parallel
This function will be called parallel
function wait(waitTime) {
return new Promise(resolve => setTimeout(() => {
console.log(`waited ${waitTime} ms`)
resolve()
}, waitTime));
}
async function serial() {
console.time('serial');
await wait(1000);
await wait(1000);
await wait(1000);
console.timeEnd('serial');
}
async function parallel() {
console.time('parallel');
await Promise.all([
wait(1000),
wait(1000),
wait(1000)
])
console.timeEnd('parallel');
}
async function test() {
await serial();
await parallel();
}
test();
Output
waited 1000 ms
waited 1000 ms
waited 1000 ms
serial: 3016.319ms
waited 1000 ms
waited 1000 ms
waited 1000 ms
parallel: 1003.017ms
From the output we can see Promise.all enables us to do all of the "wait a second" calls at the same time.
Example 2: Add two async numbers
In the previous example, we just waited a second. In this example we'll get two numbers asynchronously and add them together, serially and in parallel.
function randomNumber() {
const rand = Math.random() * 100;
return new Promise(resolve => setTimeout(() => {
resolve(rand)
}, 1000))
}
async function addExampleSerial() {
console.time('add-serial');
const number1 = await randomNumber();
const number2 = await randomNumber();
const result = number1 + number2;
console.timeEnd('add-serial');
console.log('serial result: ', result);
}
async function addExampleParallel() {
console.time('add-parallel');
const [number1, number2] = await Promise.all([randomNumber(), randomNumber()]);
const result = number1 + number2;
console.timeEnd('add-parallel');
console.log('parallel result: ', result);
}
async function test() {
await addExampleSerial();
await addExampleParallel();
}
test();
Output
add-serial: 2005.019ms
serial result: 59.0316729944722
add-parallel: 1000.616ms
parallel result: 48.7190841367634
Example 3: Required Data Dependencies
In the last example, we had to add numbers which were returned asynchronously, but we still haven't had an example where an asynchronous value is required before another asynchronous value could be retrieved.
In this example, we'll get our username, and then we'll fetch two pieces of information which depend on our username.
function fetchData(data) {
return new Promise(resolve => setTimeout(() => {
resolve(data)
}, 1000))
}
function getLoggedInUser() {
return fetchData('user1');
}
async function getDataForUser(userName) {
const profileData = await fetchData({
user1: {name: 'Micah', points: 100},
user2: {name: 'someone else', point: 200}
});
return profileData[userName];
}
async function getUserPosts(userName) {
const posts = await fetchData({
user1: ['Promises Post'],
user2: ['Streams Post']
});
return posts[userName];
}
async function userDataSerial() {
console.time('userData-serial');
const userName = await getLoggedInUser();
const userData = await getDataForUser(userName);
const userPosts = await getUserPosts(userName);
console.timeEnd('userData-serial');
}
async function userDataParallel() {
console.time('userData-parallel');
const userName = await getLoggedInUser();
const [userData, userPosts] = await Promise.all([
getDataForUser(userName),
getUserPosts(userName)
])
console.timeEnd('userData-parallel');
}
async function test() {
await userDataSerial();
await userDataParallel();
}
test();
Output
userData-serial: 3007.785ms
userData-parallel: 2006.665ms
Conclusion
In order to optimize your code for speed, pay attention to which data is required to make calls, and then structure your code to where as many of those dependencies are fetched in parallel as possible with Promise.all()
Note: At some point, you may attempt to do too many async things at once. Now you've got to determine how many you can do, and create batches of that size to prevent thrashing. That's a post for another time.
Top comments (4)
Thanks Micah.
I've never considered the implication of
await
and how it's used.This opened up a new way on how to call async methods 🙂
Nice write up!
When async/await first came out people were posting samples without even thinking abusing await's is not good!
Mixing generators too perhaps :P?
I'll be waiting that post!
A Facebook user mentioned that the third example is missing the code. Just wanted to mention.
Thanks!
Edit: Fixed it