Javascript callbacks are faster than async-await.
This benchmark can be used with node's official benchmark.js scripts The last number is the rate of operations measured in ops/sec (higher is better).
async-vs-cb.nested-benchmark.js
const common = require('./node/benchmark/common.js');
const bench = common.createBenchmark(main, {
n: [ 1400 ],
type: [ 'await-deep', 'await-shallow', 'cb-deep', 'cb-deep-promisified' ]
});
async function main(conf) {
let res = Math.random()
const string = conf.string
const type = conf.type
if (type === 'await-deep') {
bench.start();
res = await (async function nestedAsync (val, count) {
if (!count--) return val
return nestedAsync(Math.random() > Math.random() ? 1 : -1, count)
})(Math.random(), conf.n)
bench.end(conf.n);
}
if (type === 'await-shallow') {
const arr = Array.from({length: conf.n}, (v, i) => i)
const oneAsyncRes = async (val, count) => (
count + Math.random() > Math.random() ? 1 : -1)
bench.start();
for (const n of arr)
res = await oneAsyncRes(n, res)
bench.end(conf.n);
}
if (type === 'cb-deep') {
bench.start();
(function nestedCb(val, count, cb) {
if (!count--) return cb(null, val)
return nestedCb(Math.random() > Math.random() ? 1 : -1, count, cb)
})(Math.random(), conf.n, (err, res) => {
bench.end(conf.n)
})
}
if (type === 'cb-deep-promisified') {
bench.start();
const res = await new Promise(resolve => (
(function nestedCb(val, count, cb) {
if (!count--) return cb(null, val)
return nestedCb(Math.random() > Math.random() ? 1 : -1, count, cb)
})(Math.random(), conf.n, (err, res) => {
resolve(res)
})
))
bench.end(conf.n)
}
return res
}
$ node async-vs-cb.nested-benchmark.js
.js type="await-deep" n=1400: 1,516,848.9413477853
.js type="await-shallow" n=1400: 1,137,102.9378678831
.js type="cb-deep" n=1400: 2,214,625.7045278023
.js type="cb-deep-promisified" n=1400: 1,907,328.3642888186
Conclusion: performance-sensitive, deeply-stacked callback code should not be converted to async-await.
also updating benchmarks to send and receive destructured values shows significant performance drop, so that async/await with array destructuring is very slow only getting about ~200,000 ops/sec on this local machine.
To elaborate, callback functions may receive multiple values, for example,
callback(function (a, b, c, d) {
// ...
})
async function callers must receive one value and separate values are found with lookups or slower destructuring,
[a, b, c, b] = await asyncfn();
sending and receiving destructured values drops performance significantly, so that async/await with array destructuring is very slow only getting about ~200,000-300,000 ops/sec on the local machine here.