TL;DR: Shit just got streamlined. More specifically, promises start looking cleaner when you spray them with 'Callbacks Begone'.
In this post I'm going to give you a quick demo of how easy it is to incorporate ES7's proposed Async/Await functionality into your existing ES6 code. To build the code, I'm using Babel with 'es7.asyncFunctions' enabled. You can read about my Gulp setup here.
Consider the code below:
function asyncFuncA() {
return new Promise(function(r) {
setTimeout(() => { r('asyncA'); }, 2000);
});
}
function asyncFuncB() {
return new Promise(function(r) {
setTimeout(() => { r('asyncB'); }, 1000);
});
}
class AsyncController {
render(template, data) {
return new Promise(function(resolve, reject) {
// Do render stuff
resolve({ t: template, d: data });
});
}
asyncAction(route) {
return asyncFuncA()
.then(function(a) {
return asyncFuncB()
.then(b => { return [ a, b ]; });
})
.then(data => { return this.render('route', data); });
}
}
let c = new AsyncController();
c.asyncAction()
.then((obj) => { console.log(`${obj.d[0]} + ${obj.d[1]}`); });
We're looking at a Controller class with a couple of actions. The asyncAction
function of AsyncController
is what we're interested in. The action resolves two promises, one after the other, before calling render
with the results of the two promises. In a previous article, we already removed a couple of callbacks with Promise.all
.
asyncAction(route) {
return Promise.all([ asyncFuncA(), asyncFuncB() ])
.then(data => { return this.render('route', data); });
}
A lot cleaner, but we can do better. async
and await
are keywords that, when used together, allow you to write asynchronous code without callbacks. async
creates a container, within which you can execute promises (prefixed with await
) that halt the current scope, until the promises have resolved. The resulting values of said promises are returned in the same way would expect a synchronous function to behave.
What is important, is that this only happens within the async
container, which is itself becomes a promise. In the following example p
and a
are roughly equivalent.
function p() {
return Promise.resolve('hello world');
}
async function a() {
return 'hello world';
}
p().then((r) => { console.log(r); });
a().then((r) => { console.log(r); });
What I think is particularly neat, is that class functions can also be decorated with async
. So we can use this 'syntastical' sugar on our original example to create:
async asyncAction(route) {
return this.render(route, [ await asyncFuncA(), await asyncFuncB() ]);
}
The code above is equivalent to the asyncAction
functions of the previous examples. I mean, pure, wow factor. It's so awesome, I'm giddy. Deep breaths, carry on. asyncFuncA
and asyncFuncB
are both functions that return promises. These promises both return simple strings, after different timeout periods, during which time the asyncAction
function's execution is halted. After the promises has resolved, the final value is returned to current scope and execution continues, as if the await
functions were synchronous.
This new functionality has taken promises to a whole new level for me. The async
function by itself, removes the need for repetitive Promise
declarations. Combined with await
, we get asynchronous code that is as easy to read as synchronous code. And no callbacks!