top of page

Understanding Async/Await in Understanding Async/Await in JavaScript in 10 Minutes

For a long time, we had to rely on callbacks to handle asynchronous code in JavaScript. This often resulted in messy and hard-to-maintain code, especially when dealing with nested callbacks, which many of us have found frustrating.


Callbacks have several drawbacks. When we have multiple asynchronous operations, callbacks must wait for each other to complete, extending the overall completion time. Additionally, writing nested callbacks can make our code complex and difficult to maintain.

Fortunately, in ES6, JavaScript introduced Promises (via .then()). This was a significant improvement over callbacks, and most of the community quickly adopted Promises. While Promises made the code easier to read and maintain, they didn't completely solve the issues associated with callbacks.


Finally, in the latest ES7 version, Async/Await was introduced, providing a better way to write asynchronous code in JavaScript. Async/Await makes the code cleaner and easier to use.


JavaScript

What is Async/Await Javascript?

Async/Await is a JavaScript feature that helps us work with asynchronous functions in a more straightforward and understandable way. It is built on Promises and is compatible with all Promise-based APIs.

  • Async: Declares an asynchronous function using async function someName() {...}. It automatically converts a regular function into a Promise. When calling an async function, it handles everything and returns the result within the function. Async also allows the use of Await.

  • Await: Pauses the execution of an async function. When placed before a Promise, it waits until the Promise is resolved and returns the result. Await only works with Promises and can only be used inside async functions.

Here's a simple example to clarify:

Suppose we want to fetch some JSON files from our server. We'll write a function using the axios library to send an HTTP GET request to https://tutorialzine.com/misc/files/example.json. We need to wait for the server's response since this is an asynchronous HTTP request.

Below, we'll write the same function in two different ways: the first using Promises, and the second using Async/Await.

// Method 1: Using Promises
function getJSON() {
    // To make the function blocking we manually create a Promise.
    return new Promise(function(resolve) {
        axios.get('https://tutorialzine.com/misc/files/example.json')
            .then(function(json) {
                // The data from the request is available in a .then block
                // We return the result using resolve.
                resolve(json);
            });
    });
}
// Method 2: Using Async/Await
async function getJSONAsync() {
    // The await keyword saves us from having to write a .then() block.
    let json = await axios.get('https://tutorialzine.com/misc/files/example.json');
    // The result of the GET request is available in the json variable.
    // We return it just like in a regular synchronous function.
    return json;
}// Method 1: Using Promises
function getJSON() {
    // To make the function blocking we manually create a Promise.
    return new Promise(function(resolve) {
        axios.get('https://tutorialzine.com/misc/files/example.json')
            .then(function(json) {
                // The data from the request is available in a .then block
                // We return the result using resolve.
                resolve(json);
            });
    });
}
// Method 2: Using Async/Await
async function getJSONAsync() {
    // The await keyword saves us from having to write a .then() block.
    let json = await axios.get('https://tutorialzine.com/misc/files/example.json');
    // The result of the GET request is available in the json variable.
    // We return it just like in a regular synchronous function.
    return json;
}

Both functions above perform the same task - they return Promises and resolve with the JSON response from axios. However, the Async/Await version is shorter and easier to read.

We can call our async function as follows:

getJSONAsync().then(function(result) {
    // Do something with the result.
});

Does Async/Await Make Promises Obsolete?

Not entirely. When working with Async/Await, we are still using Promises under the hood. Therefore, a good understanding of Promises is still beneficial.

Moreover, there are scenarios where Async/Await may not be suitable, and Promises must be used. For example, when we need to perform multiple asynchronous operations and wait for all of them to complete. Using Async/Await in such cases may not be optimal:

async function getABC() {
    let A = await getValueA(); // getValueA takes 2 seconds to finish
    let B = await getValueB(); // getValueB takes 4 seconds to finish
    let C = await getValueC(); // getValueC takes 3 seconds to finish
    return A * B * C;
}

Each await call will wait for the previous one to complete. This sequential execution results in a total execution time of 9 seconds (2 + 4 + 3). This is not optimal since A, B, and C do not depend on each other, and we don't need to wait for A's value before obtaining B's.

In such cases, using Promises is more appropriate. To perform all requests simultaneously, we can use Promise.all(). This ensures that we have all results before continuing execution, but the asynchronous operations run in parallel rather than sequentially:

async function getABC() {
    // Promise.all() allows us to send all requests at the same time.
    let results = await Promise.all([getValueA(), getValueB(), getValueC()]);
    return results.reduce((total, value) => total * value);
}

This way, the execution time is reduced. getValueA and getValueC will complete before getValueB finishes. Instead of taking 9 seconds, the function will only take 4 seconds to wait for all three functions to return values.

Error Handling in Async/Await

Another great feature of Async/Await is that it allows us to catch unexpected errors using try/catch. We simply wrap our await calls in a try/catch block as follows:

async function doSomethingAsync() {
    try {
        // This async call may fail.
        let result = await someAsyncCall();
    } catch (error) {
        // If it does, we will catch the error here.
    }
}

The catch block will handle errors caused by asynchronous functions or any errors written inside the try block.

In some situations, we can also catch errors when calling the async function. Since all async functions return Promises, we can use the .catch() method when calling them:

// Async function without a try/catch block.
async function doSomethingAsync() {
    // This async call may fail.
    let result = await someAsyncCall();
    return result;
}
// We catch the error upon calling the function.
doSomethingAsync()
    .then(successHandler)
    .catch(errorHandler);

Depending on the specific situation, we will use either try/catch or .catch() to catch and handle errors. However, we should not use both at the same time as it may lead to unexpected issues.

Browser Support

Async/Await is supported in most major browsers, except for IE11. All modern browsers recognize your async/await code without the need for external libraries. Node.js also supports async/await starting from Node 8 and later versions.


If this does not meet your needs, there are also some JavaScript transpilers like Babel and TypeScript, as well as the Node.js async/await library, which provide platform-specific versions of this feature.


By understanding and utilizing Async/Await, you can write cleaner, more maintainable, and more efficient asynchronous JavaScript code.

Comments


bottom of page