async / await
async functions, await, Promise.resolve, Promise.all, new Promise, setTimeout / clearTimeout, and node:fs/promises run with Node's ordering: synchronous code first, then microtasks (every await yields one, even on a settled promise), then timers in deadline order. A rejected promise makes await throw, so try/catch works across await.
// async/await with Node's ordering: synchronous code runs before any awaited continuation,
// Promise.all keeps input order, a rejection is caught by try/catch around the await, and
// timers run after the microtasks drain.
async function score(name: string): Promise<number> {
console.log("scoring", name);
return name.length * 10;
}
async function offline(): Promise<number> {
throw new Error("offline");
}
async function main(): Promise<void> {
console.log("start");
const scores = await Promise.all([score("slow"), score("fast")]);
console.log("scores", scores);
try {
await offline();
} catch (e) {
console.log("caught", String(e));
}
}
setTimeout(() => {
console.log("timer fires last");
}, 0);
main();
console.log("main() returned first");start
scoring slow
scoring fast
main() returned first
scores [ 40, 40 ]
caught Error: offline
timer fires lastnew Promise
new Promise works when the executor is written inline, with resolve (and reject, if you use it) annotated with its function type and the executor returning void:
new Promise<T>((resolve: (value: T) => void, reject: (reason: Error) => void): void => {
// ...
});reject may be left out. A typical use wraps a callback API such as setTimeout:
// new Promise with the executor written inline and its parameters annotated.
function delay(ms: number, label: string): Promise<string> {
return new Promise<string>(
(resolve: (value: string) => void, reject: (reason: Error) => void): void => {
if (ms < 0) {
reject(new RangeError(`negative delay: ${ms}`));
return;
}
setTimeout(() => resolve(label), ms);
},
);
}
async function main(): Promise<void> {
console.log(await delay(20, "slow"), await delay(0, "fast"));
try {
await delay(-1, "never");
} catch (e) {
console.log(String(e));
}
}
main();slow fast
RangeError: negative delay: -1How it runs
Each async call runs on its own stack (a fiber, ucontext underneath). await on a pending promise switches back to the event loop, and settling the promise queues the waiting fiber as a microtask. No state-machine transform is applied to your function, so an async body compiles like any other function. The loop drains microtasks, then timers and I/O, until nothing is pending.
An unhandled rejection ends the program with Node's exit code.
Limits today
new Promisewith an executor that is not written inline, or whoseresolvehas no type annotation, is rejected (CS1000); the message shows the form to write..then()on a promise is rejected (CS1225); useawait.Promise<null>is rejected (CS1233); usePromise<void>orPromise<string | null>.Promise.reject,Promise.raceandPromise.allSettledare rejected (CS1220); throw inside an async function instead ofPromise.reject.awaitat the top level of a module is rejected (CS1000); put the code inasync function main()and call it.setTimeout(async () => ...)is rejected (CS1231); a rejection inside it would have nothing to await it.
Next: Errors.