Understand async/await better

August 29, 2017 0 Comments

Understand async/await better

 

 

Async/Awaitfinally comes to Node V8 after quite a period of time. There are plenty of articles on Medium blogging about why they are better than promise-based asyc solutions and how you should use them. If you unfamiliar with async/await, take your time to read these articles to wrap your head around it and then come back to this article.

In this article, I will cover some aspects you need to be aware of when working with async/await.

Code below will be used throughout this blog.

const getProduct = product => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (product) {
resolve({
product
});
} else {
reject(new Error('product not specified'));
}
}, 1000);
});
};
function getProductNames() {
return Promise.resolve(['apple', 'pear', 'mango']);
}

Basically, awaitis followed by a promise instance and returns the resolved value upon execution.

This means you cannot chain returned value using thenlike you would with Promise.

async function printProduct() {
const product = await getProduct('pineapple');
product.then() // error then is not a function!
}

Let’s tweak getProducta bit to resolve with another promise.

...
if (product) {
resolve(new Promise((resolve, reject) => {
setTimeout(x => {
resolve({
product
});
}, 5000);
}));
}
...

Now, let’s try it again. Oops! In 5 seconds time, you still see the same error again since what await does is like — I promise that I will wait for no matter how long I need to before it gets resolved.

This allows you to chain promise using then:

async function printProduct() {
return await getProduct('pineapple');
}
var prod = printProduct();
prod.then(x => {
console.log(x);
});

This also allows for another way of handling errors apart from try/catchthat we all know:

async function printProduct() {
console.log(afffa);
return await getProduct('pineapple');
}
var prod = printProduct();
prod.catch(err => {
console.log(err); // error caught!
});

Let’s call getProducton each of products returned by calling getProductNamesand see what will happen.

async function printProduct() {
const products = await getProductNames();
products.forEach(async x => {
const prod = await getProduct(x);
console.log(prod);
});
}

Console prints out the output below in one go after 1 second.

{ product: 'apple' }
{ product: 'pear' }
{ product: 'mango' }

This is working fine but not what we actually expect to see. Instead, we want them to happen in order one after another. The problem with using normal forEachis that it does not wait until the previous promise is resolved before it starts to run the next one.

Given the fact that Array itself has iteratorinterface and can be looped using for...of. So the solution is to use for...ofwhich is designed to work with collections like Array, Map, Set, Generator. What it does is it will call nextto move onto next element when the previous one has been resolved.

async function printProduct() {
const products = await getProductNames();
for (const r of products) {
const readContents = await getProduct(r);
console.log(readContents);
}
}

Now we are talking and seeing each fruit one second at a time.

Other than running them in a series, we can also run them in parallel:

async function printProduct () {
const products = await getProductNames();
const promises = products.map(p => getProduct(p));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
}

Alternatively, we can use Promise.allto achieve the same behaviour.

async function printProduct () {
const products = await getProductNames();
const promises = products.map(p => getProduct(p));
let results = [];
results = await Promise.all(promises);
}

Async/Await really makes our async operations much easier. Hopefully, this article makes you have a better understanding of how Async/Awaitworks so that you can start using it if you haven’t.


Tag cloud