Skip to Content

Cleaning up asynchronous JavaScript with async/await keywords

Capgemini
2018-07-11
Asynchronous JavaScript

Software applications often work synchronously in that they start a task and then must wait for it to finish before starting a new task. While that is the only choice for some types of processing, an alternative using asynchronous processing can be a better solution as it allows the application to perform multiple tasks simultaneously. In asynchronous algorithms, multiple tasks can be started but eventually, processing can only continue when all of them have completed. This speeds up application performance but does require coordination to ensure that all started tasks have completed before the application is allowed to continue with the processing of the results. Consider this Node.js application:

This example uses the fs-extra node module to look for a directory, create one if it doesn’t exist, empty that directory if there are any files in it, create a newfile.txt, and then read that file.

When running this code, the console should print:

  • Created/Checked for Directory
  • Emptied Data
  • File Created
  • File Contents: Hello World how are you??

Anyone who has encountered asynchronous code, though, will know that this is not the case. Here is an example of what is logged:

  • Created/Checked for Directory
  • Emptied Data
  • { Error: ENOENT: no such file or directory, open ‘./data/newfile.txt’
  •  errno: -2,
  •  code: ‘ENOENT’,
  •  syscall: ‘open’,
  •  path: ‘./data/newfile.txt’ }
  • File Created

The code above is attempting to read the newfile.txt before the outputFile method has finished creating it.

Enter the Promise

In JavaScript, a Promise is a solution to the asynchronous problem discussed above and allows the application to coordinate it’s processing as each asynchronous task completes. A Promise represents the future state of an asynchronous process once it has completed. The Microsoft Developer’s Network has an in-depth definition here.

In the following example, Promises are used to make sure each asynchronous step in creating the file is completed before the next one begins.

The code above produces this output:

  • Created/Checked for Directory
  • Emptied Data
  • File Created
  • File Contents:  Hello World how are you??

Mission accomplished! Our asynchronous code now fires and completes as intended. Unfortunately, the code is a little messy and difficult to read because of the conditional logic (.then statements in the chain of promises) required to coordinate between each of the steps. Fortunately, there is another JavaScript construct we can use which provides the same functionality but in code that is much cleaner, easier to understand, and more maintainable for any future changes.

The Solution: Async/Await

To clean up the promise chain above, you can use the Async and Await keywords. A function declared with the ‘async’ keyword indicates that the function will be handling asynchronous operations. The declaration would look like this:

  • async function foo() {
  • //asynchronous code here
  • }

The await keyword tells JavaScript to wait for a Promise to complete before continuing processing and is applicable only when nested inside a function declared with the ‘async’ keyword. See this example:

  • Async function foo() {
  • await promise;
  • }

Here is our code using async and await:

Much cleaner! This implements exactly the same functions as the example above while using 30 fewer lines of code. In addition, readability and maintainability are enhanced by eliminating the nested chain of promises.

Also, notice that we can still chain .then after an await to have code fire directly following the resolution of a promise. So, if you find yourself with asynchronous JavaScript and you need to use a promise, consider using async/await to produce cleaner and easier asynchronous code.