JavaScript Goes Asynchronous (and It’s Awesome)

By David Catuhe

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:

  • ElDerecho

    I can’t get worked up about promises. They basically move where you declare the callback. Await seems like it will be a great improvement though and will significantly reduce code complexity.

    • m3l

      not exactly. Promises are a huge step forward. Chaining, spread/all, centralized error handling, good stack traces for async codes etc… They’re not just a way to rewrite callbacks

    • quazecoatl

      I have kind of same feeling, I don’t see promises as a huge step forward.
      ES7’s await is the way to go, but I wonder how safe is it is to use it today by babel.

  • rahilwazir

    Please correct the formatting of the code

  • Tiago Braga

    I’ve been using ES6 for a while, and I’ve got to say: It’s a lot more intuitive than ES5.
    I didn’t know about async and await ES7 features, and I can’t wait for the new spec!

  • cswam

    If you really want to understand Asynchronous JavaScript Object Management visit the AJOMS Website and learn and experience it in action (EXAMPLE). Don’t confuse Microsoft salesmanship with comprehension, it just ” PROMISES” confusion.

  • ajoms

    If you want to understand Asynchronous JavaScript Object Managememt (AJOMS), visit the AJOMS Web-site and read the AJOMS Framework Tutorial that describes the AJOMS unique asynchronous Web-site Framework software and play with the example of that employs its power.

  • https://www.codepunker.com/ Daniel Gheorghe

    Hi,

    Thanks for the article. It’s informative.

    Typo:
    “It provides two functions that developers has to call” … I guess it’s “have”.

    Cheers,

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Front-end, once a week, for free.