Top Level Await

A module with a top-level `await` runs as a coroutine, exactly as an engine evaluates it: the body runs synchronously up to its first await, and the code after `await p` is a reaction on p — ordered among p's other reactions by when each was registered.

const log: string[] = [];

function later(ms: number, value: string): Promise<string> {
  return new Promise<string>((resolve) => setTimeout(() => resolve(value), ms));
}

// 1. The continuation of `await p` runs after a `.then` registered before the
//    await, and before one registered after it.
const config = later(5, "ready");
config.then((v) => { log.push("then-before: " + v); });
const state = await config;
log.push("after await: " + state);
config.then(() => { log.push("then-after"); });

// 2. Awaiting a plain value or a settled promise still takes one tick.
Promise.resolve().then(() => { log.push("queued job"); });
await 0;
log.push("after tick");

// 3. `for await` at top level.
for await (const n of [later(1, "a"), later(1, "b")]) {
  log.push("for await: " + n);
}

// 4. Closures over top-level bindings keep working after the body has ended.
let beats = 0;
const timer = setInterval(() => {
  beats++;
  if (beats === 3) {
    clearInterval(timer);
    console.log(log.join("\n"));
    console.log("beats: " + beats);
  }
}, 2);
log.push("body done");
View source on GitHubexamples/async/top_level_await.ts