How V8 Optimizes Async/Await: Performance Boosts from Node 8 to 12

This article explains how V8’s compiler, garbage collector, and a bug‑inspired optimization improve async/await performance across Node.js versions, detailing benchmark results, code behavior differences, and the engine’s internal steps for handling await.

Node Underground
Node Underground
Node Underground
How V8 Optimizes Async/Await: Performance Boosts from Node 8 to 12

Async Introduction

Async functions use an implicit Promise to perform asynchronous operations and return their result, making asynchronous code appear synchronous, simplifying syntax, and allowing try/catch for error handling.

Async performance across Node versions

The benchmark shows that lower execution time means better performance; V8 noticeably improved async code speed from v5.5 to v6.8.

Reasons for performance improvement

Overall gains stem from three areas:

New optimizing compiler TurboFan

New garbage collector Orinoco

An optimization inspired by the Node 8 await bug

const p = Promise.resolve();
(async () => {
  await p;
  console.log('after:await');
})();
p.then(() => console.log('tick:a')).then(() => console.log('tick:b'));

In Node 8 the output order is "after:await" then the two "tick" logs, but Node 10 follows the specification and prints the two "tick" logs before "after:await" because the earlier behavior was a bug.

V8’s handling of await follows these steps for a simple async function async function foo(v) { const w = await v; return w; }:

async function foo(v) {
  const w = await v;
  return w;
}

Mark the function as resumable (it can pause and later resume).

Create an implicit Promise that will be returned to the caller.

Convert the argument v to a Promise, wrapping it if it isn’t already one.

Attach handlers to the Promise so the function can resume later.

Suspend the async function and return the implicit Promise.

When the Promise fulfills, resume execution, assign its value to w, and continue.

Even when v is already a Promise, V8 originally created an extra wrapper, adding overhead. The newer optimization skips this extra wrapper, saving a Promise allocation and two micro‑task ticks, which both improves performance and restores the Node 8 logging order while remaining spec‑compliant. This optimization is enabled by default starting with Node.js 12.

PerformanceNode.jsV8await
Node Underground
Written by

Node Underground

No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.