JavaScript Shinies: Async/Await

An ongoing series of JS features that are here or coming, and this one is pretty thoroughly usable in production. Async and Await are paired keywords that make asynchronous behaviour a lot more logical.

We’re starting off fairly gently with a JavaScript feature that’s pretty much landed. You shouldn’t need to even use something like Babel for this to work.

The Problem We Solve With It

componentWasCreated(props) {
const post = http.get(`http://api/posts/?slug=${props.slug}`);
return http.get(`http://myapi/posts/${post.id}/comments`);
}

This may (and should) give people some twitches. This is definitively asynchronous code. What’s going to happen here isn’t super obvious and is a trap for young players. What I expect to happen as a naive assumption is that it will get the post as an ajax request, and then use the post object returned to get an id, which it then uses to get comments for that post. (Note that I don’t return the post object because I don’t care about it, I just want the comments. No idea why, this is bullshit code.)

I’ve written about promises before and I don’t want to labour it, so the solution here is that the http library above returns promises, and the promise is “chained” with the then function. To rewrite the above code we want to nest this functionality accordingly.

componentWasCreated(props) {
return http.get(`http://api/posts/?slug=${props.slug}`).then(
function(post){
return http.get(`http://myapi/posts/${post.id}/comments`);
}
);
}

This will now do as expected. I think. To be completely honest I’m not 100% sure. Promises when used this way aren’t particularly intuitive. Am I returning the promise? Or the result of the promise? Yes I am! Aside from that, the continual nesting inside a then call is also a fairly unintuitive pattern, and can lead to some deep and fairly impenetrable code, that often works the reverse of what you would expect.

Enter Await

async componentWasCreated(props) {
const post = await http.get(`http://api/posts/?slug=${props.slug}`);
return await http.get(`http://myapi/posts/${post.id}/comments`);
}

Note that by using this await keyword all our promises kind of do what we expect them to. The async elements no longer return promises that we then have to do something with, they return the result of the call we made.

Await is a blunt instrument. Once you discover it, it can be tempting to use it all the time, because it flattens out any async issues. But bear in mind that using await basically makes your code synchronous. While there are plenty of cases where you want to flatten out the asynchrony there are also cases where you don’t. If your code makes sense to keep asynchronous, and do things in parallel, you will lose that benefit by using await.

For the most part, though, it’s truly invaluable. I do a lot of unit testing of blockchain functionality, which involves typically using JavaScript to call the blockchain as an RPC request, and then doing a bunch of assertions on the values. The use of async and await means these tests can look relatively simple and comprehensible. Instead of this.

it("should add a new product", function() {  var itemName = "TestItem";
var itemPrice = 1000;
HashMarket.deployed().then(function(instance) {
instance.addNewItem(itemName, itemPrice).then(function(item) {
instance.getItem(item.id).then(function(result) {
var [price, seller] = result;
assert.equal(itemPrice, price, "Price not properly added");
assert.equal(itemSeller, seller, "Seller not added");
});
});
});
});

By using Async/Await (as well as other ES6+ features) we can simplify this code out a lot. Not only do we stop the chaining and nesting, but we also remove some temporary variables, and clean the overall structure.

it("should add a new product", function() {  const itemName = "TestItem";
const itemPrice = 1000;
const instance = await HashMarket.deployed();
const itemId = await instance.addNewItem(itemName, itemPrice);
const [price, seller] = await instance.getItem(itemId);
assert.equal(itemPrice, price, "Price wasn't properly added");
assert.equal(itemSeller, seller, "Seller not added");
});

Definitely an improvement.

Enabling This Feature

Senior Web Developer based in Bangkok, Thailand. Javascript, Web and Blockchain Developer.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store