Performance test of async / await in Node.js 8 LTS

The recently released Node.js 8 LTS comes with async / await support, which turns promises from an ugly kludge into something actually useful.

This post shows an example of using the new async / await feature and compares performance to a traditional callback implementation.

TL;DR

When tested against an HTTP server running on localhost the promises implementation was about 2% slower, which is, for the most part, negligible.

Benchmark

The test scenario involves making three HTTP requests in sequence, using the result of the previous request as the path for the next one. This is a simplified version of a standard real-life use-case of assembling some data from several different API endpoints.

Callback version

const http = require('http');

function makeRequest (path, callback) {
    const o = {
        method: 'GET',
        port: 8080,
        path: path
    };

    const req = http.request(o, (res) => {
        const data = [];

        res.on('data', (buffer) => {
            data.push(buffer);
        });

        res.on('end', () => {
            callback(undefined, Buffer.concat(data).toString());
        });
    });

    req.on('error', (err) => {
        callback(err);
    });

    req.end();
}

function getData (callback) {
    makeRequest('/a', (err, path1) => {
        if (err) {
            return callback(err);
        }

        makeRequest(path1, (err, path2) => {
            if (err) {
                return callback(err);
            }

            makeRequest(path2, (err, path3) => {
                if (err) {
                    return callback(err);
                }

                callback(undefined, path3);
            });
        });
    });
}

Promise version

const http = require('http');

function makeRequest(path) {
    return new Promise((resolve, reject) => {
        const o = {
            method: 'GET',
            port: 8080,
            path: path
        };

        const req = http.request(o, (res) => {
            const data = [];

            res.on('data', (buffer) => {
                data.push(buffer);
            });

            res.on('end', () => {
                resolve(Buffer.concat(data).toString());
            });
        });

        req.on('error', (err) => {
            reject(err);
        });

        req.end();
    });
}

async function getData (callback) {
    let path;

    try {
        path = await makeRequest('/a');
        path = await makeRequest(path);
        path = await makeRequest(path);

    } catch (e) {
        callback(e);
    }

    callback(undefined, path);
}

Benchmark code

const results = [];

(function loop (i) {
    if (i) {
        let start = Date.now();

        getData((err) => {
            if (err) {
                throw err;
            }

            results.push(Date.now() - start);

            process.nextTick(() => {
                loop(i - 1);
            });
        });

    } else {
        console.log(results.slice(10).reduce((sum, n) => {
            return sum + n;
        }, 0));
    }
}(100010));

HTTP server code

var http = require('http');

const server = http.createServer((req, res) => {
    if (req.url === '/a') {
        res.end('/b');
    } else if (req.url === '/b') {
        res.end('/c');
    } else if (req.url === '/c') {
        res.end('/d');
    }
});

server.listen(8080);

Results

Node version 8.9.1 was used in this test.

Callback version

51616
51504
52370
51137
51477
51574
51818
51019
51053
51458

Avg: 51502.6

Promise version

52701
52447
52264
52816
52913
52637
52104
52659
52193
52330

Avg: 52506.4