Performance
The mean time of six benchmarks, with plain JavaScript, the transform only, and the library.
The benchmarks
The command pnpm bench starts each case in a separate Node.js process. Each process measures each benchmark with tinybench. A value is the mean time of one operation of the benchmark. The command does 5 rounds, with a different order of the cases in each round, and a value is the median of the rounds. The rounds decrease the effect of other work on the machine.
| Benchmark | What it does |
|---|---|
| await loop | One async function with 10,000 await steps. |
| async calls | 10,000 async functions in sequence, each with one await. |
| then chain | A chain of 10,000 then callbacks. |
| concurrent tasks | 1,000 async functions at the same time, each with 10 await steps, in one context. |
| request handlers | 1,000 request handlers, each in its own context, with three await steps and one read of the context. |
| get in nested contexts | 100,000 reads with get(), 5 contexts deep. |
The cases
| Case | What it measures |
|---|---|
| Plain JavaScript | The same code without the library. A Variable class without propagation gives the same API. |
| Transform only | The Babel preset with a plain coroutine. This case shows the cost of the transform without the cost of the context. |
| Library | The Babel preset and the browser runtime. |
The tools wrote this data at 8 Oct 2026, 22:10 UTC.
Node.js v25.2.1, Windows_NT 10.0.26200, AMD Ryzen AI 7 PRO 350 w/ Radeon 860M.
The mean time of each benchmark
Show the data as a table
| Benchmark | Case | Mean time | Factor |
|---|---|---|---|
| await loop | Plain JavaScript | 0.26 ms | 1× |
| await loop | Transform only | 0.31 ms | 1.22× |
| await loop | Library | 0.4 ms | 1.56× |
| async calls | Plain JavaScript | 0.61 ms | 1× |
| async calls | Transform only | 0.99 ms | 1.61× |
| async calls | Library | 1.2 ms | 1.99× |
| then chain | Plain JavaScript | 0.19 ms | 1× |
| then chain | Transform only | 0.19 ms | 1.04× |
| then chain | Library | 0.33 ms | 1.77× |
| concurrent tasks | Plain JavaScript | 0.39 ms | 1× |
| concurrent tasks | Transform only | 0.49 ms | 1.24× |
| concurrent tasks | Library | 0.68 ms | 1.75× |
| request handlers | Plain JavaScript | 0.31 ms | 1× |
| request handlers | Transform only | 0.41 ms | 1.3× |
| request handlers | Library | 0.6 ms | 1.9× |
| get in nested contexts | Plain JavaScript | 0.1 ms | 1× |
| get in nested contexts | Transform only | 0.11 ms | 1× |
| get in nested contexts | Library | 0.39 ms | 3.74× |
What the results tell
- The transform. Each async function of transformed code is a generator that the coroutine operates. This costs more than a native async function. The number of microtask steps of each
awaitdoes not change. - The context. Each
thencallback and each step of the coroutine sets the context and sets it again after the step. Eachthencall makes two small wrappers. get(). One read takes approximately 4 ns. The plain case reads one field, so the factor is large, but the time is small.
Note
The values are microbenchmarks. The benchmarks do no other work, so the factors are the largest possible factors. Measure your own application before and after you add the library.