Demonstrate common async operations with both async.js and ES6 async-await.
Node.js v7.6.0 brings official support for async functions. This is an ES7 feature that allows handling asynchronous operations in a clean way.
Here is an overview of major weapons at our disposal to fight callback hell:
- The popular async package
- fibers package in node.js
- Promise - since ES6 (Node.js v0.12)
- Generators - since ES6 (Node.js v4)
- Async Functions - since ES7 (Node.js v7.6.0)
Here is a basic example of an async function:
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
async function answer() {
console.log('Start');
await sleep(2000);
console.log('End');
return 42;
}
var p = answer();
assert(p instanceof Promise);
p.then(result => {
assert.equal(result, 42);
console.log('OK');
}).catch(err => console.error(err));
It all starts and ends with a promise:
- await takes a promise and suspends the current script until the promise is fulfilled. Then the script is resumed and await returns the resolved value or throws the reject reason.
- await can appear only in an async function
- async function always returns a promise, so one can await it
Note that await does not block the event queue, so node.js can process other events while waiting for the promise.
We can easily promisify existing callback APIs using packages like pify.
const assert = require('assert');
const pify = require('pify');
const fs = pify(require('fs'));
async function main() {
let text = await fs.readFile(__filename, 'utf8');
assert(/some-token/.test(text));
try {
await fs.readFile('no-such-file', 'utf8');
assert(false, 'should throw');
} catch (err) {
assert.equal(err.code, 'ENOENT');
}
}
Execute several operations sequentially, each one taking the result from the previous one.
function calc(x, cb) {
async.waterfall([
inc.bind(null, x),
double
], cb);
}
async function calc(x) {
let r = await inc(x);
return await double(r);
}
Start several operations in parallel and wait all of them to complete.
function calc(x, cb) {
async.parallel([
inc.bind(null, x),
double.bind(null, x)
], cb);
}
async function calc(x) {
return await Promise.all([inc(x), double(x)]);
}
Start several operations in parallel and get the result of the first one to complete.
function calc(x, y, cb) {
async.race([
inc.bind(null, x),
double.bind(null, y)
], cb);
}
async function calc(x, y) {
return await Promise.race([inc(x), double(y)]);
}
Execute the same operation for each array element in parallel.
function calc(arr, cb) {
async.map(arr, inc, cb);
}
async function calc(arr) {
return await Promise.all(arr.map(inc));
}
Execute the same operation for each array element sequentially.
function calc(arr, cb) {
async.mapSeries(arr, inc, cb);
}
async function calc(arr) {
return await bluebird.mapSeries(arr, inc);
}
6 Reasons Why JavaScript’s Async/Await Blows Promises Away
While it compares async functions to promises, the same issues are valid also with callbacks.