Mastering Asynchronous Javascript: Part 4 – async / await

Last time we talked about a new feature introduced in the ES6 language called generators. These make asynchronous programming fairly easy, but have the drawback of needing to use a module to make them work for asynchronous flow control (I showed you co) because they’re actually iterators rather than being designed to handle asynchronous coding.

This time I will teach you about a new feature in the very latest version of JavaScript, ES7, which was explicitly created for asynchronous programming. This feature is referred to as async / await.

When using async / await, things are actually very similar to using generators in combination with co. Checkout out the same database code I’ve used in all of the previous articles, but this time implemented using async / await:

// Make db functions return promises
var promisify = require("promisify-node");
var MongoClient = promisify("mongodb").MongoClient;

var url = 'mongodb://localhost:27017/myproject';
function output (user, orders) {
    // output data in pretty format
}

function logError (error) {
    // Log the error (could be console, a service, a file etc)
}

async function run () {
    const userId = process.argv[2];
    try {
        const db = await MongoClient.connect(url);
        await db.collection('access_log').insert({ userId, date: new Date() });
        const users = await db.collection('users').find({ userId }).toArray();
        const orders = await db.collection('orders').find({ userId }).toArray();
        output(user, orders);
    } catch (e) {
        logError(e);
    }
}

run();

Some important things to know about this code:

  • The ‘await’ keyword can only be used within functions that are marked async with the ‘async’ keyword, like the run function in the example.
  • ‘await’ can only be used to the left of a promise (or a function call that returns a promise). The execution of the function will pause and wait for the promise to be either resolved or rejected.
  • Resolved promises that have been awaited evaluate to the value they resolve with. If there is an uncaught exception within an awaited function call, the promise will be rejected with that exception, and then it will be thrown in the async function.
  • All async functions automatically return a promise when called. They can return another value within the body of the function, and this value will be used to resolve the promise the function actually returns.
  • Since async functions return promises, calls to async functions can themselves be awaited.

The following example shows how async functions return promises, and how return values within async functions are handled:

async function foo () {
	return 'Foo return!';
}

// Since foo() returns a promise resolved with the return value of calling foo()
// We can use then() to get the return value of foo()
// Logs 'Foo return!'
foo().then(function (r) { console.log(r); ]);

async function bar () {
    // Since foo() returns a promise, we can await it in an async function,
	// and get the return value that way
	var r = await foo();
	console.log(r);
}

//Logs 'Foo return!'
bar();

Next Time

In the fifth and final post of the series I will show you one final method for asynchronous programming, and that is observables (in particular, RxJs). I do think that either generators or async await really are the way to go, but I want to cover observables because it’s possible to do some things with them that you can’t do with other asynchronous paradigms. Although a lot more complicated that async await or generators, observables are more powerful. It is also very possible you’ll have to use them in a real world job, as many large JavaScript projects use RxJs, so knowing about it increases your employability.

The full series:

  1. Callbacks
  2. Promises
  3. Generators
  4. Async / await
  5. RxJS

3 comments

Leave a Reply

Your email address will not be published. Required fields are marked *