async/await in the event loop: what really happens at each await
await pauses only the async function and resumes it in a microtask. See why code after await runs later, and why forEach does not wait.
Comes up in: JavaScript, Coding
Learn#
Kabir writes items.forEach(async (item) => await save(item)) and then shows “All saved!”. Users see the message before anything is saved. To fix it, you need to know what await does to the event loop.
await pauses only the current async function. That is called suspension: its frame leaves the call stack and the rest of the function — the continuation — is saved. Everything else keeps running. When the awaited value is ready, the continuation is queued as a microtask.
An async function returns a promise as soon as it first pauses. Its return value settles that promise later.
Even await on a plain value like 42 pauses until the next microtask checkpoint.
async function load() {
console.log("2 load starts");
const x = await 42;
console.log("4 resumed with", x);
return "done";
}
console.log("1 script");
const p = load();
console.log("3 script continues", p instanceof Promise);
p.then((v) => console.log("5", v));
setTimeout(() => console.log("6 timer"), 0);Output (verified)
1 script 2 load starts 3 script continues true 4 resumed with 42 5 done 6 timer
So forEach never waits: each callback pauses at await and returns a promise that forEach ignores. Use for...of with await for one-at-a-time work.
Call stack
- script
- load
Microtasks
empty
Console
- 1 script
- 2 load starts
The script calls load(). Its body runs synchronously until the first await.
Read the steps as text
- The script calls load(). Its body runs synchronously until the first await.
- await pauses load. Its frame leaves the stack; the rest of load is saved for later.
- load already returned a pending promise, so the script carries on.
- The script ends. The microtask resumes load, which prints and returns 'done'.
- Returning settles load's promise, so its .then callback runs next. The timer, a task, comes last.
Predict#
Predict first
What does this print? Type the letters with spaces.
async function f() {
console.log("B");
await null;
console.log("D");
}
console.log("A");
f();
console.log("C");f runs synchronously up to the first await, so B prints right after A. Then f pauses, the script prints C, and D runs in a microtask. The tempting “A B D C” assumes await null does not pause — it always does.
Interview angle#
Say it like this — about 30 seconds
“An async function runs synchronously until its first
await. Then it suspends: its frame leaves the stack, the rest is saved as a continuation, and the caller gets a pending promise. When the awaited promise settles, the continuation is queued as a microtask and resumes there. So code afterawaitalways runs later, even forawait null. Areturnresolves the function’s promise. That is whyforEachwith an async callback does not wait.”
Follow-up What does this print: console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4);
Follow-up How is await built on promises?
await x turns x into a promise and attaches the continuation with the same machinery as .then, so resuming is a promise job — a microtask.Follow-up How do you run async work in parallel instead of in series?
await Promise.all(promises) — or use a pool for a limit (JS.11.07).Practice#
faded
Fix mapSeries: it must process items one at a time, in order, and resolve only when all are done.
Done when
- The results array matches the input order and is complete when the promise resolves.
- Only one call to
fnruns at a time, and items start in input order.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What does await pause?
Only the current async function. The rest of the program keeps running.
How does an async function resume after await?
Its continuation is queued as a microtask when the awaited value settles.
Why does forEach not wait for async callbacks?
It ignores the promises the callbacks return.
What does this print?(async () => { console.log("x"); await 0; console.log("z"); })();
console.log("y");
(async () => { console.log("x"); await 0; console.log("z"); })();
console.log("y");x y z
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
An async function runs like normal code until await. There it hands control back, and the caller gets a promise. The rest of the function resumes in a microtask once the value is ready.
Check yourself: Does code after `await null` run in the same task?
Yes, but later in it — at the microtask checkpoint, after the current code finishes.
How it works underneathSenior rounds
The spec’s Await step converts the value to a promise and attaches the continuation as a promise reaction Source: ECMA-262: Await. So resuming is a promise job, run at the microtask checkpoint like any .then callback.
Check yourself: Which machinery does await reuse to resume?
Promise reactions — the same jobs that run .then callbacks.
Edge cases and trapsWhere people slip
Awaiting a native promise used to cost three microtask ticks. A 2018 change to the spec cut it to one, so every engine now behaves like this Source: V8 blog: Faster async functions and promises:
const p = Promise.resolve();
(async () => { await p; console.log("after await"); })();
p.then(() => console.log("then 1")).then(() => console.log("then 2"));Output (verified)
after await then 1 then 2
V8 shipped it first, in version 7.2.
Check yourself: Why do old blog posts show `then 1, then 2` printing before `after await`?
Before a 2018 spec change, await on a native promise took three microtask ticks; now it takes one.
In productionStaff-level answers
Unawaited promises cause “saved” messages before saving and errors nobody catches. Turn on @typescript-eslint/no-floating-promises and no-misused-promises. Use for...of with await for order, Promise.all for speed, and a pool when an API limits you.
Check yourself: Name one tool that fails your build when you forget to await a promise.
The TypeScript ESLint rule no-floating-promises.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is the output of:
console.log(1); setTimeout(()=>console.log(2)); Promise.resolve().then(()=>console.log(3)); console.log(4);and why? - How does
awaitwork under the hood in terms of the event loop?