JavaScript Goes Asynchronous (and It’s Awesome)

Share this article

This article is part of a web development series from Microsoft. Thank you for supporting the partners who make SitePoint possible.
JavaScript has come a long way since its early versions and thanks to all efforts done by TC39 (The organization in charge of standardizing JavaScript (or ECMAScript to be exact) we now have a modern language that is used widely. One area within ECMAScript that received vast improvements is asynchronous code. You can learn more about asynchronous programming here if you’re a new developer. Fortunately we’ve included these changes in Windows 10’s new Edge browser—check out the Microsoft Edge change log. Among all these new features, let’s specifically focus on “ES2016 Async Functions” behind the Experimental Javascript features flag and take a journey through the updates and see how ECMAScript can improve your currently workflow.

First stop: ECMAScript 5 – Callbacks city

ECMAScript 5 (and previous versions as well) are all about callbacks. To better picture this, let’s have a simple example that you certainly use more than once a day: executing a XHR request.

var displayDiv = document.getElementById("displayDiv");

// Part 1 - Defining what do we want to do with the result
var processJSON = function (json) {
var result = JSON.parse(json);

    result.collection.forEach(function(card) {
var div = document.createElement("div");
        div.innerHTML = card.name + " cost is " + card.price;

        displayDiv.appendChild(div);
    });
}

// Part 2 - Providing a function to display errors
var displayError = function(error) {
    displayDiv.innerHTML = error;
}

// Part 3 - Creating and setting up the XHR object
var xhr = new XMLHttpRequest();

xhr.open('GET', "cards.json");

// Part 4 - Defining callbacks that XHR object will call for us
xhr.onload = function(){
if (xhr.status === 200) {
        processJSON(xhr.response);
    }
}

xhr.onerror = function() {
    displayError("Unable to load RSS");
}

// Part 5 - Starting the process
xhr.send();
Established JavaScript developers will note how familiar this looks since XHR callbacks are used all the time! It’s simple and fairly straight forward: the developer creates an XHR request and then provides the callback for the specified XHR object. In contrast, callback complexity comes from the execution order which is not linear due to the inner nature of asynchronous code: ECMAScript 5 XHR request callback The “callbacks hell” can even be worse when using another asynchronous call inside of your own callback.

Second stop: ECMAScript 6 – Promises city

ECMAScript 6 is gaining momentum and Edge is has leading support with 88% coverage so far. Among a lot of great improvements, ECMAScript 6 standardizes the usage of promises (formerly known as futures). According to MDN, a promise
is an object which is used for deferred and asynchronous computations. A promise represents an operation that hasn’t completed yet, but is expected in the future. Promises are a way of organizing asynchronous operations in such a way that they appear synchronous. Exactly what we need for our XHR example. Promises have been around for a while but the good news is that now you don’t need any library anymore as they are provided by the browser. Let’s update our example a bit to support promises and see how it could improve the readability and maintainability of our code:

var displayDiv = document.getElementById("displayDiv");

// Part 1 - Create a function that returns a promise
function getJsonAsync(url) {
// Promises require two functions: one for success, one for failure
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();

        xhr.open('GET', url);

        xhr.onload = () => {
if (xhr.status === 200) {
// We can resolve the promise
resolve(xhr.response);
            } else {
// It's a failure, so let's reject the promise
reject("Unable to load RSS");
            }
        }

        xhr.onerror = () => {
// It's a failure, so let's reject the promise
reject("Unable to load RSS");
        };

        xhr.send();
    });
}

// Part 2 - The function returns a promise
// so we can chain with a .then and a .catch
getJsonAsync("cards.json").then(json => {
var result = JSON.parse(json);

    result.collection.forEach(card => {
var div = document.createElement("div");
        div.innerHTML = `${card.name} cost is ${card.price}`;

        displayDiv.appendChild(div);
    });
}).catch(error => {
    displayDiv.innerHTML = error;
});
You may have noticed a lot of improvements here. Let’s have a closer look.

Creating the promise

In order to “promisify” (sorry but I’m French so I’m allowed to invent new words) the old XHR object, you need to create a Promise object: ECMAScript 6 creating Promise Object

Using the promise

Once created, the promise can be used to chain asynchronous calls in a more elegant way: Using Promise Object to chain asynchronous calls So now we have (from the user standpoint):
  • Get the promise (1)
  • Chain with the success code (2 and 3)
  • Chain with the error code (4) like in a try/catch block
What’s interesting is that chaining promises are easily called using .then().then(), etc. Side node: Since JavaScript is a modern language, you may notice that I’ve also used syntax sugar from ECMAScript 6 like template strings or arrow functions.

Terminus: ECMAScript 7 – Asynchronous city

Finally, we’ve reached our destination! We are almost in the future, but thanks to Edge’s rapid development cycle, the team is able to introduce a bit of ECMAScript 7 with async functions in the latest build! Async functions are a syntax sugar to improve the language-level model for writing asynchronous code. Async functions are built on top of ECMAScript 6 features like generators. Indeed, generators can be used jointly with promises to produce the same results but with much more user code. We do not need to change the function which generates the promise as async functions work directly with promise. We only need to change the calling function:

// Let's create an async anonymous function
(async function() {
try {
// Just have to await the promise!
var json = await getJsonAsync("cards.json");
var result = JSON.parse(json);

        result.collection.forEach(card => {
var div = document.createElement("div");
            div.innerHTML = `${card.name} cost is ${card.price}`;

            displayDiv.appendChild(div);
        });
    } catch (e) {
        displayDiv.innerHTML = e;
    }
})();
This is where magic happens. This code looks like a regular synchronous code with a perfectly linear execution path: Creating async function Quite impressive, right? And the good news is that you can even use async functions with arrow functions or class methods.

Going further

If you want more detail on how we implemented it in Chakra, please check the official post on the Microsoft Edge blog.You can also track the progress of various browsers implementation of ECMAScript 6 and 7 using Kangax’s website. Feel free also to check our JavaScript roadmap as well! Please, do not hesitate to give us your feedback and support your favorite features by using the vote button: JavaScript Roadmap vote button Thanks for reading and we’re eager to hear your feedback and ideas!

More hands-on with Web Development

This article is part of the web development series from Microsoft tech evangelists on practical JavaScript learning, open source projects, and interoperability best practices including Microsoft Edge browser and the new EdgeHTML rendering engine. We encourage you to test across browsers and devices including Microsoft Edge – the default browser for Windows 10 – with free tools on dev.modern.IE: In-depth tech learning on Microsoft Edge and the Web Platform from our engineers and evangelists: More free cross-platform tools & resources for the Web Platform:

Frequently Asked Questions about JavaScript Asynchronous Programming

What is the difference between synchronous and asynchronous programming in JavaScript?

In synchronous programming, tasks are executed one after another, meaning a task must be completed before the next one starts. This can lead to blocking or delay in execution if a task takes too long to complete. On the other hand, asynchronous programming allows tasks to be executed concurrently. This means that if a task is waiting for some operation, like data from an API, it doesn’t block the execution of other tasks. JavaScript uses callbacks, promises, and async/await to handle asynchronous operations.

How does the async function work in JavaScript?

The async function in JavaScript is used to define an asynchronous function that returns an implicit Promise as its result. When the async function is called, it runs the code within the function. If the function returns a value, the Promise will be resolved with the returned value, but if the async function throws an error, the Promise will be rejected with that error.

What is the role of the await operator in asynchronous JavaScript?

The await operator is used with an async function to pause the execution of the function and wait for the Promise’s resolution or rejection. It returns the resolved value of the Promise. If the value is not a Promise, it converts the value to a resolved Promise. If the Promise is rejected, the await expression throws the rejected value.

How can I handle errors in asynchronous JavaScript?

Error handling in asynchronous JavaScript can be done using try/catch blocks within async functions. If an error is thrown within the try block, execution is immediately shifted to the catch block. If a Promise is rejected within an async function, it can be caught in the catch block.

What are callbacks in JavaScript and how are they related to asynchronous programming?

Callbacks are functions that are passed as arguments to other functions and are invoked after some operation has been completed. In JavaScript, callbacks are often used for asynchronous operations. They allow the function to be executed once the asynchronous operation is complete and a response is ready.

What are Promises in JavaScript?

Promises in JavaScript represent a completion or failure of an asynchronous operation. They return a value which is either a resolved value or a reason for failure. Promises have three states: pending, fulfilled, and rejected. They help in managing asynchronous operations and provide better error handling than callbacks.

How can I use Promises to handle multiple asynchronous operations in JavaScript?

JavaScript provides Promise.all() and Promise.race() methods to handle multiple asynchronous operations. Promise.all() waits for all promises to be resolved or for any promise to be rejected. If the returned promise resolves, it is an array of the values from the resolved promises in the same order. Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.

What is the event loop in JavaScript?

The event loop is a mechanism in JavaScript that constantly checks the call stack to see if there’s any function that needs to run. While doing so, it also checks the task queue where asynchronous callbacks are placed once they’re ready to be executed. If the call stack is empty, the event loop dequeues the tasks from the task queue to the call stack to be executed.

How does JavaScript handle asynchronous operations with the help of the event loop?

JavaScript uses the event loop in conjunction with the task queue to handle asynchronous operations. When an asynchronous operation is initiated, it is sent out of the current execution and waits for the operation to complete. Once the asynchronous operation is complete, its callback function is sent to a task queue. The event loop constantly checks the call stack and the task queue. If the call stack is empty, it takes the first task from the queue and pushes it to the call stack, which then starts executing.

Can I use async/await with forEach in JavaScript?

No, forEach is not promise-aware and won’t wait for your async function to finish before moving on to the next item in the array. Instead, you can use for…of or map() with Promise.all() to handle asynchronous operations within a loop.

David CatuheDavid Catuhe
View Author

David Catuhe is a Principal Program Manager at Microsoft focusing on web development. He is author of the babylon.js framework for building 3D games with HTML5 and WebGL. Read his blog on MSDN or follow him on Twitter.

asynchronousECMAScript5ECMAScript6ECMAScript7JavaScriptmdn
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week