Async Patterns
Concurrency and cancellation patterns that fit the Node event loop. Results appear in the same fence: same-line // comments when short, multiline // blocks below the sample when not.
Search across all documentation pages
Concurrency and cancellation patterns that fit the Node event loop. Results appear in the same fence: same-line // comments when short, multiline // blocks below the sample when not.
Mark functions async and await promises instead of nesting then callbacks.
async function load(id: string) {
return { id, ok: true };
}
const user = await load("1");
user
// { id: "1", ok: true }Wait for many tasks without failing fast - inspect each result status.
const results = await Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error("x")),
]);
results[0].status // "fulfilled"
results[1].status // "rejected"Promise.all rejects on the first failure - use when all must succeed.
const [a, b] = await Promise.all([
Promise.resolve(1),
Promise.resolve(2),
]);
a + b // 3Promise.any resolves with the first fulfillment.
const first = await Promise.any([
Promise.reject(new Error("a")),
Promise.resolve("ok"),
]);
first // "ok"Built-in timeouts via AbortSignal.timeout for fetch and many Node APIs.
const signal = AbortSignal.timeout(50);
await new Promise((r) => setTimeout(r, 0));
signal.aborted // false until timeout fires
// await fetch(url, { signal });Limit parallel work with a simple pool to protect downstream systems.
async function mapPool<T, R>(items: T[], limit: number, fn: (t: T) => Promise<R>) {
const ret: R[] = [];
let i = 0;
await Promise.all(Array.from({ length: limit }, async () => {
while (i < items.length) {
const idx = i++;
ret[idx] = await fn(items[idx]);
}
}));
return ret;
}
await mapPool([1, 2, 3], 2, async (n) => n * 2)
// [2, 4, 6]Produce and consume values over time with async generators.
async function* gen() {
yield 1;
yield 2;
}
const xs: number[] = [];
for await (const x of gen()) xs.push(x);
xs // [1, 2]setImmediate yields to the event loop phase; nextTick drains sooner - prefer Immediate for fairness.
const order: string[] = [];
process.nextTick(() => order.push("tick"));
setImmediate(() => order.push("immediate"));
await new Promise((r) => setImmediate(r));
order
// ["tick", "immediate"]Wrap legacy error-first callbacks with util.promisify.
import { promisify } from "node:util";
const wait = promisify((ms: number, cb: (e: Error | null) => void) => setTimeout(cb, ms, null));
await wait(1);
// resolves after 1msExpose resolve/reject only when bridging event emitters carefully.
function deferred<T>() {
let resolve!: (v: T) => void;
let reject!: (e: unknown) => void;
const promise = new Promise<T>((res, rej) => {
resolve = res; reject = rej;
});
return { promise, resolve, reject };
}
const d = deferred<number>();
d.resolve(7);
await d.promise // 7Retry idempotent operations with exponential delay and jitter.
async function retry<T>(fn: () => Promise<T>, n = 3): Promise<T> {
let err: unknown;
for (let i = 0; i < n; i++) {
try { return await fn(); }
catch (e) { err = e; await new Promise((r) => setTimeout(r, 10 * 2 ** i)); }
}
throw err;
}
let tries = 0;
await retry(async () => {
tries++;
if (tries < 2) throw new Error("fail");
return "ok";
})
// "ok"Serialize critical sections with a promise chain lock.
let chain: Promise<unknown> = Promise.resolve();
function withLock<T>(fn: () => Promise<T>) {
const run = chain.then(fn, fn);
chain = run.then(() => {}, () => {});
return run;
}
await withLock(async () => 1) // 1Use timers/promises for awaitable delays and intervals.
import { setTimeout as sleep } from "node:timers/promises";
const t0 = Date.now();
await sleep(10);
Date.now() - t0 >= 10 // trueLog rejections from batch jobs without aborting siblings.
const results = await Promise.allSettled([
Promise.resolve("a"),
Promise.reject(new Error("b")),
]);
results.filter((r) => r.status === "fulfilled").length // 1Initialize config at module load with top-level await in ESM.
const config = await Promise.resolve({ port: 3000 });
config.port // 3000Stack versions: Node.js 24.18.0 (LTS line 24) · TypeScript 5.6+ · npm 10+
Reviewed by Chris St. John·Last updated Jul 18, 2026