The Beauty of Async-Await

Async/await is a new way to write asynchronous code. The usual methods of writing asynchronous code include callbacks and promises. Async/await uses the power of promises but presents the code in a very synchronous way making it more readable and clear.

In this blog, we will see the best way to use the power of this tool named Async-Await to bring out the best in our applications.

Lets’s take an example of old school code:

const makeACall = () =>
 getData()
 .then(data => {
 console.log(data);
 return data;
 })

The above code using async/await will be written as below:

const makeACall = async () => {
 var data= await getData();
 console.log(data);
 return data;
}

You will see how this difference in the code clarity and readability improves with the increasing complexity of the callbacks and promise-chains.

You will comprehend the power with more scenarios and examples.

Scenario 1:

Function A returns a Promise, then function B needs the value returned by function A and function C needs the resolved value of both function A’s and function B’s Promise.

async function executeAsyncTask () {
 const valueA = await fnA()
 const valueB = await fnB(valueA)
 return fnC(valueA, valueB)
}

You can think of all the ways you could have done this with promises if anything was as simple!

Scenario 2:

Function A, Function B and Function C return Promises, then the function D needs the value returned by function A, function B and function C for further computations.

There is no dependency between A,B and C and they should run in parallel to avoid any unnecessary wait time.

async function execParallelAsyncTasks () { 
const [ valueA, valueB, valueC ] = await Promise.all([ fnA(), fnB(), fnC() ])
 fnD(valueA,valueB,valueC)
 }

When you want to run async operations in parallel, you should always wrap them into Promise.all() to avoid any intermediate wait time. This combines the power of async-await with promises.

Scenario 3:

Class A has a function B which is asynchronous and returns a promise.

class A{
 async fnB() {
 return await Promise.resolve('Returned');
 }
}

new A()
 .fnB()
 .then(alert); // 'Returned'

Await can be used in any asynchronous function call within a function which is defined using async. Await cannot be used at top-level without an async function.

Scenario 4:

In order to handle Errors which occur in a chain of promises returned, it is as simple as synchronous code, using the try-catch block.

const makeACall = async () => {
 try {
 const data = JSON.parse(await getJSON())
 const another = await getData(data)
 console.log(data, another)
 } catch (err) {
 console.log(err)
 }
}

Another way to handle this could be on the async function call using a .catch() as below

makeACall().catch(alert);

If we forget to add .catch here also, then we get an unhandled promise error in the console. We can catch such errors using a global event handler as described in the Promises chaining.

Scenario 5:

If you need to work with an array of items asynchronously or perform a set of asynchronous operations, for e.g. reading a set of files and logging the data.

5a) If you need to perform the asynchronous operations in sequence using async/await:

async function printFiles () {
 const files = await getFilePaths();

for (let file of files) {
 const contents = await fs.readFile(file, 'utf8');
 console.log(contents);
 }
}

5b) If you need to perform the set of asynchronous operations like reading the files, in parallel:

async function printFiles () {
 const files = await getFilePaths();

await Promise.all(files.map(async (file) => {
 const contents = await fs.readFile(file, 'utf8')
 console.log(contents)
 }));
}

You should be careful in making use of async-await inside a loop and appropriately put the usage based on the requirement of parallel or sequential execution.

Conclusion

Async-await provides a great way to write asynchronous code that is easy both to read and write.

With async/await we rarely need to write promise.then/catch, but we should always remember that they are based on promises and they return promises only, and we may need to handle the promise in the outermost call.

Also Promise.all is a must use thing to wait for many tasks simultaneously and avoid any unnecessary wait times impacting the performance of the application.

References

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s