Firefox 52 also includes a brand new JavaScript feature from ES2017: asynchronous functions and their companion, the await
keyword. Async functions build on top of ES2015 Promises, allowing authors to write asynchronous sequences in a similar way to how they would write their synchronous equivalents.
Take the following example, which takes the result of one asynchronous request, modifies it, and passes it as the argument to a second asynchronous function. Here's how it would look with a traditional callback approach:
function doThings() {
doThingA(function (err, result) {
if (err) {
handleError(err);
} else {
doThingB(result + 1, function (err, newResult) {
if (err) {
handleError(err)
} else {
useResult(newResult);
}
});
}
});
}
Relatively straightforward, but if we were to need to do additional processing and asynchronous requests, the levels of nesting or series of callback functions could become difficult to manage. Also, with more complex callback sequences, it can become difficult to determine the flow of the code, making debugging difficult.
Promises, introduced in ES105, allow for a more compact representation of the same flow:
function doThings() {
doThingA()
.then(function (result) {
return doThingB(result + 1);
})
.then(useResult)
.catch(handleError);
}
Promises excel at simplifying these sequential method sequences. In this example, instead of passing a function to doThingA
and doThingB
, the functions now return a Promise, which will be resolved when the function's result is availasble. However, when additional processing or conditional calls are required, the nesting can still become quite deep and control flow can again be hard to follow.
Async functions allow us to re-write the example to resemble the way we would write a synchronous equivalent:
async function doThings() {
try {
let result = await doThingA();
useResult(await doThingB(result + 1));
} catch (err) {
handleError(err);
}
}
The async
keyword in front of the function tells the JS engine that the following function can be paused by asynchronous requests, and that the result of the function will be a Promise. Each time we need to wait for an asynchronous result, we use the await
keyword. This will pause execution of the function without stopping other functions from running. Also, doThingA
and doThingB
don't need to be changed from how they would be written in the Promise example.
Async functions aren't a cure-all for complex control flow, but for many cases can simplifty the authoring and maintenance of async code, without importing costly libraries.