Js Event loop questions!!

In a recent interview, my friend was asked an interesting question — and I got it wrong too at first. The trick was that async has higher priority than setTimeout. I later realized why: async returns a Promise, and Promises are handled in the microtask queue, which runs before the macrotask queue where setTimeout lives. That moment led me to create a whole practice sheet on JS task priorities — it really clears up how things actually execute in JavaScript.
So here, we won’t just solve questions — we’ll also use diagrams to understand exactly how the code runs inside the browser engine.
Before we dive into the questions, let’s quickly understand the different queues in JavaScript and how they work together inside the browser engine.
The Big Picture
┌────────────────────────────┐
│ JS Engine │
│ (Call Stack + Event Loop) │
└─────────────┬──────────────┘
│
┌─────────┴─────────┐
│ │
Microtask Queue Macrotask Queue
(High Priority) (Low Priority)
│ │
Promises, async/await setTimeout, setInterval, I/O
1. Call Stack
This isn’t a queue — it’s where your JavaScript runs line by line.
When the call stack is empty, the event loop picks tasks from the queues.
2. Macrotask Queue (Task Queue)
Holds tasks that should run after the current stack finishes.
Includes:
setTimeoutsetIntervalsetImmediate(Node.js only)DOM events (
click,scroll)AJAX callbacks
3. Microtask Queue
Reserved for high-priority async tasks that execute before any macrotasks.
Includes:
Promises (
.then,.catch,.finally)async/await(sinceawaitreturns a promise)queueMicrotask()MutationObserver(browser API)
4. Render Queue (Browser Only)
After microtasks and before macrotasks, the browser may:
Update the DOM
Perform reflows
Repaint elements
You can’t directly add tasks here — it’s part of the browser’s rendering cycle.
Simplified Priority Overview
| Queue Type | Examples | Priority | When It Runs |
| Call Stack | Normal JS code | Highest | Immediately |
| Microtask | Promises, async/await, queueMicrotask | High | After current stack, before macrotasks |
| Macrotask | setTimeout, setInterval, I/O | Medium | After microtasks are done |
| Render Queue | DOM painting | Medium | Between micro/macro cycles |
| Animation Frame | requestAnimationFrame | Medium | Before browser repaint |
Event Loop Flow
┌───────────────────────┐
│ Call Stack (Run JS) │
└────────────┬──────────┘
↓
┌───────────────────────┐
│ Microtask Queue │ ← Promise, async/await
└────────────┬──────────┘
↓
┌───────────────────────┐
│ Render (Browser) │
└────────────┬──────────┘
↓
┌───────────────────────┐
│ Macrotask Queue │ ← setTimeout, I/O
└───────────────────────┘
We’ll mainly focus on the first three — the Call Stack, Microtask Queue, and Macrotask Queue — since understanding them clearly will help you master JavaScript’s execution order.
Now we have a slightly better picture of the queues and their priority, so let's start with the questions.
console.log('script start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('script end');
// script start
// script end
// promise
// timeout
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('script start') | — | — |
| ⏩ After exec | — | — | script start |
| 2 | setTimeout() | Web API Timer: [console.log('timeout')] | script start |
| 3 | Promise.resolve() → .then() | Web API Timer: [console.log('timeout')]Microtask Queue: [console.log('promise')] | script start |
| 4 | console.log('script end') | Web API Timer: [console.log('timeout')]Microtask Queue: [console.log('promise')] | script start |
| ⏩ After exec | — | Web API Timer: [console.log('timeout')]Microtask Queue: [console.log('promise')] | script start, script end |
| 5 | — (Stack empty) | 🟢 Event Loop picks Microtasks | script start, script end |
| 6 | console.log('promise') | Web API Timer: [console.log('timeout')] | script start, script end |
| ⏩ After exec | — | Web API Timer: [console.log('timeout')] | script start, script end, promise |
| 7 | console.log('timeout') (Macrotask dequeued) | — | script start, script end, promise |
| ⏩ After exec | — | — | script start, script end, promise, timeout |
Step-by-Step Explanation
Step 1 — console.log('script start')
The line
console.log('script start')is placed on the Call Stack and executed immediately because it’s synchronous.It prints
'script start'to the Console.After printing, the stack becomes empty again.
✅ Console now shows: script start
Step 2 — setTimeout(...)
The
setTimeout()function is added to the Call Stack.The browser (or Node’s environment) moves the callback function
() => console.log('timeout')to the Web API Timer to wait for 0 milliseconds.After setting up the timer,
setTimeout()itself finishes executing and is removed from the stack.
✅ Web API Timer holds: console.log('timeout')
✅ Console remains: script start
Step 3 — Promise.resolve().then(...)
Promise.resolve()runs instantly and returns a resolved Promise.The
.then()callback (() => console.log('promise')) is placed into the Microtask Queue.The promise itself doesn’t run immediately — it waits until the Call Stack is empty.
✅ Microtask Queue holds: console.log('promise')
✅ Web API Timer still has: console.log('timeout')
Step 4 — console.log('script end')
The
console.log('script end')runs next — it’s still synchronous.'script end'is printed to the Console.The Call Stack is now completely empty.
✅ Console now shows: script start, script end
Step 5 — Stack Empty → Event Loop Checks Queues
The Call Stack is empty, so the Event Loop starts checking queues.
It always gives priority to Microtasks (like Promises) before Macrotasks (like
setTimeout).
✅ Next task to run → console.log('promise') from the Microtask Queue.
Step 6 — Run Microtask (console.log('promise'))
The promise callback moves from the Microtask Queue to the Call Stack.
It executes
console.log('promise'), printing'promise'to the console.After execution, the Microtask Queue becomes empty.
✅ Console now shows:script start, script end, promise
Step 7 — Run Macrotask (console.log('timeout'))
Now that all Microtasks are done, the Event Loop moves to the Macrotask Queue.
The callback from
setTimeoutis taken from the Web API Timer and moved into the Call Stack.It executes
console.log('timeout'), printing'timeout'.After it finishes, everything is empty again.
async function foo() {
console.log('foo start');
await null;
console.log('foo end');
}
console.log('start');
foo();
console.log('end');
// start
// foo start
// end
// foo end
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | foo() | — | start |
| 3 | Inside foo: console.log('foo start') | — | start |
| ⏩ After exec | — | — | start, foo start |
| 4 | await null | Microtask Queue: [console.log('foo end')] | start, foo start |
| ⏩ After exec | — | Microtask Queue: [console.log('foo end')] | start, foo start |
| 5 | console.log('end') | Microtask Queue: [console.log('foo end')] | start, foo start |
| ⏩ After exec | — | Microtask Queue: [console.log('foo end')] | start, foo start, end |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (foo end) | start, foo start, end |
| 7 | console.log('foo end') | — | start, foo start, end |
| ⏩ After exec | — | — | start, foo start, end, foo end |
Step-by-Step Explanation
Step 1 — console.log('start')
- The first synchronous statement runs immediately and prints "start".
✅ Console:start
Step 2 — foo()
The async function
foo()is called.It enters the Call Stack, and execution begins inside
foo.
Step 3 — Inside foo: console.log('foo start')
- This line executes synchronously and prints "foo start".
✅ Console:start, foo start
Step 4 — await null
The
awaitexpression pausesfoo()execution.It schedules the continuation (
console.log('foo end')) as a Microtask and returns a pending Promise immediately.The Call Stack clears — the rest of
foo()(foo end) will run later.
✅ Microtask Queue:console.log('foo end')
Step 5 — console.log('end')
- The next synchronous line after
foo()runs, printing "end".
✅ Console:start, foo start, end
Step 6 — Stack Empty → Event Loop runs Microtasks
After all synchronous code finishes, the Event Loop checks the Microtask Queue.
It finds the continuation of
foo()waiting there.
Step 7 — Run Microtask → console.log('foo end')
- The resumed part of
foo()executes, printing "foo end".
✅ Final Console:start, foo start, end, foo end
async function foo() {
console.log('foo');
}
async function bar() {
console.log('bar start');
await foo();
console.log('bar end');
}
bar();
console.log('script');
// bar start
// foo
// script
// bar end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | bar() | — | — |
| 2 | Inside bar: console.log('bar start') | — | — |
| ⏩ After exec | — | — | bar start |
| 3 | Inside bar: await foo() → calls foo() | — | bar start |
| 4 | Inside foo: console.log('foo') | — | bar start |
| ⏩ After exec | — | — | bar start, foo |
| 5 | foo() returns resolved Promise → resumes later | Microtask Queue: [console.log('bar end')] | bar start, foo |
| 6 | console.log('script') | Microtask Queue: [console.log('bar end')] | bar start, foo |
| ⏩ After exec | — | Microtask Queue: [console.log('bar end')] | bar start, foo, script |
| 7 | — (Stack empty) | 🟢 Event Loop picks Microtask (bar end) | bar start, foo, script |
| 8 | console.log('bar end') | — | bar start, foo, script |
| ⏩ After exec | — | — | bar start, foo, script, bar end |
Step-by-Step Explanation
Step 1 — Call bar()
The function
bar()is invoked.It enters the Call Stack.
Execution begins inside
bar.
Step 2 — console.log('bar start')
This synchronous line runs immediately.
It prints "bar start" to the console.
After execution, control moves to the next line (
await foo()).
✅ Console: bar start
Step 3 — await foo()
The function
foo()is called as part of theawaitexpression.foo()enters the Call Stack and executes synchronously until it hits anawaitor finishes.
Step 4 — Inside foo: console.log('foo')
This line executes synchronously and prints "foo".
After this,
foo()completes and returns a resolved Promise (because async functions always return Promises).
✅ Console: bar start, foo
Step 5 — Resume behavior of await foo()
The
awaitpauses the rest ofbar()until the Promise returned byfoo()resolves.The continuation (
console.log('bar end')) is scheduled as a Microtask.bar()returns a pending Promise and exits the Call Stack.
✅ Microtask Queue: console.log('bar end')
Step 6 — console.log('script')
After
bar()finishes scheduling the microtask, the next synchronous line runs.It prints "script" immediately.
✅ Console: bar start, foo, script
Step 7 — Stack Empty → Event Loop runs Microtasks
Once all synchronous tasks are complete, the Event Loop checks the Microtask Queue.
It finds the pending microtask from
bar()continuation.
Step 8 — Run Microtask → console.log('bar end')
The resumed part of
bar()executes.It prints "bar end" to the console.
All queues are now empty.
✅ Final Console:bar start, foo, script, bar end
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => console.log('setTimeout'), 0);
async1();
new Promise(resolve => {
console.log('promise1');
resolve();
}).then(() => console.log('promise2'));
console.log('script end');
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('script start') | — | — |
| ⏩ After exec | — | — | script start |
| 2 | setTimeout() | Web API Timer: [console.log('setTimeout')] | script start |
| 3 | async1() | — | script start |
| 4 | Inside async1: console.log('async1 start') | — | script start |
| ⏩ After exec | — | — | script start, async1 start |
| 5 | Inside async1: await async2() → calls async2() | — | script start, async1 start |
| 6 | Inside async2: console.log('async2') | — | script start, async1 start |
| ⏩ After exec | — | Microtask Queue: [console.log('async1 end')] | script start, async1 start, async2 |
| 7 | new Promise(...) | — | script start, async1 start, async2 |
| 8 | Inside Promise executor: console.log('promise1') | — | script start, async1 start, async2 |
| ⏩ After exec | — | Microtask Queue: [console.log('async1 end'), console.log('promise2')] | script start, async1 start, async2, promise1 |
| 9 | console.log('script end') | Microtask Queue: [console.log('async1 end'), console.log('promise2')] | script start, async1 start, async2, promise1 |
| ⏩ After exec | — | Microtask Queue: [console.log('async1 end'), console.log('promise2')] | script start, async1 start, async2, promise1, script end |
| 10 | — (Stack empty) | 🟢 Event Loop picks Microtasks (async1 end, then promise2) | script start, async1 start, async2, promise1, script end |
| 11 | Run Microtask → console.log('async1 end') | Microtask Queue: [console.log('promise2')] | script start, async1 start, async2, promise1, script end |
| ⏩ After exec | — | Microtask Queue: [console.log('promise2')] | script start, async1 start, async2, promise1, script end, async1 end |
| 12 | Run Microtask → console.log('promise2') | — | script start, async1 start, async2, promise1, script end, async1 end |
| ⏩ After exec | — | — | script start, async1 start, async2, promise1, script end, async1 end, promise2 |
| 13 | Run Macrotask (from Timer): console.log('setTimeout') | — | script start, async1 start, async2, promise1, script end, async1 end, promise2 |
| ⏩ After exec | — | — | script start, async1 start, async2, promise1, script end, async1 end, promise2, setTimeout |
Step-by-Step Explanation
Step 1 — console.log('script start')
Runs first (synchronous code).
Prints “script start”.
✅ Console:script start
Step 2 — setTimeout(..., 0)
setTimeoutschedules its callback in the Web API Timer for later (Macrotask).It doesn’t block — returns immediately.
✅ Web API Timer:console.log('setTimeout')
Step 3 — async1() call
async1()enters the Call Stack and starts executing.
Step 4 — Inside async1: console.log('async1 start')
- Runs synchronously and prints “async1 start”.
✅ Console:script start, async1 start
Step 5 — await async2()
Calls
async2()immediately.Execution inside
async1pauses at theawait.
Step 6 — Inside async2: console.log('async2')
Executes synchronously and prints “async2”.
Returns a resolved Promise.
async1’s continuation (console.log('async1 end')) is placed in the Microtask Queue.
✅ Console:script start, async1 start, async2
✅ Microtask Queue:console.log('async1 end')
Step 7 — new Promise(...)
- The Promise constructor executes its function immediately (synchronously).
Step 8 — Inside Promise executor: console.log('promise1')
Prints “promise1” synchronously.
When
resolve()is called, the.then()callback (console.log('promise2')) is queued as a Microtask.
✅ Microtask Queue:console.log('async1 end'),console.log('promise2')
✅ Console:script start, async1 start, async2, promise1
Step 9 — console.log('script end')
Synchronous line — runs immediately.
Prints “script end”.
✅ Console:script start, async1 start, async2, promise1, script end
Step 10 — Stack Empty → Event Loop checks queues
The Call Stack is empty.
The Event Loop picks Microtasks first (before Macrotasks).
Microtasks to run (in order):
async1 endpromise2
Step 11 — Run Microtask 1 → console.log('async1 end')
- Prints “async1 end”.
✅ Console:script start, async1 start, async2, promise1, script end, async1 end
Step 12 — Run Microtask 2 → console.log('promise2')
- Prints “promise2”.
✅ Console:script start, async1 start, async2, promise1, script end, async1 end, promise2
Step 13 — Run Macrotask → console.log('setTimeout')
After all microtasks finish, the Event Loop executes the Macrotask from the timer.
Prints “setTimeout”.
✅ Final Console:
script start, async1 start, async2, promise1, script end, async1 end, promise2, setTimeout
console.log('start');
Promise.resolve().then(() => console.log('promise'));
setTimeout(() => console.log('timeout'));
console.log('end');
// start
// end
// promise
// timeout
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | Promise.resolve().then(...) | Microtask Queue: [console.log('promise')] | start |
| 3 | setTimeout(...) | Web API Timer: [console.log('timeout')] | start |
| 4 | console.log('end') | Web API Timer: [console.log('timeout')] Microtask Queue: [console.log('promise')] | start |
| ⏩ After exec | — | Web API Timer: [console.log('timeout')] Microtask Queue: [console.log('promise')] | start, end |
| 5 | — (Stack empty) | 🟢 Event Loop picks Microtask (promise) | start, end |
| 6 | console.log('promise') | Web API Timer: [console.log('timeout')] | start, end |
| ⏩ After exec | — | Web API Timer: [console.log('timeout')] | start, end, promise |
| 7 | Run Macrotask → console.log('timeout') | — | start, end, promise |
| ⏩ After exec | — | — | start, end, promise, timeout |
Step-by-Step Explanation
Step 1 — console.log('start')
The first synchronous statement runs immediately.
Prints “start” to the console.
✅ Console:start
Step 2 — Promise.resolve().then(...)
The
Promise.resolve()instantly returns a resolved Promise.The
.then()callback (console.log('promise')) is scheduled in the Microtask Queue to run after all synchronous code completes.
✅ Microtask Queue:console.log('promise')
Step 3 — setTimeout(...)
The
setTimeoutcallback is sent to the Web API Timer.It will move to the Macrotask Queue after the delay (0ms).
✅ Web API Timer:console.log('timeout')
Step 4 — console.log('end')
Another synchronous statement.
Runs immediately and prints “end”.
✅ Console:start, end
Step 5 — Stack Empty → Event Loop checks Queues
After all synchronous code is done, the Call Stack becomes empty.
The Event Loop checks for pending tasks.
Microtasks (Promises) have higher priority than Macrotasks (setTimeout).
✅ Next to run:console.log('promise')
Step 6 — Run Microtask (console.log('promise'))
The promise callback executes from the Microtask Queue.
Prints “promise”.
✅ Console:start, end, promise
Step 7 — Run Macrotask (console.log('timeout'))
After all Microtasks are complete, the Event Loop picks the next Macrotask from the Timer Queue.
Prints “timeout”.
✅ Final Console:
start, end, promise, timeout
async function a() {
await b();
console.log('a end');
}
async function b() {
console.log('b');
}
console.log('start');
a();
console.log('end');
// start
// b
// end
// a end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | a() | — | start |
| 3 | Inside a: await b() → calls b() | — | start |
| 4 | Inside b: console.log('b') | — | start |
| ⏩ After exec | — | Microtask Queue: [console.log('a end')] | start, b |
| 5 | console.log('end') | Microtask Queue: [console.log('a end')] | start, b |
| ⏩ After exec | — | Microtask Queue: [console.log('a end')] | start, b, end |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (a end) | start, b, end |
| 7 | console.log('a end') | — | start, b, end |
| ⏩ After exec | — | — | start, b, end, a end |
Step-by-Step Explanation
Step 1 — console.log('start')
The first synchronous statement executes immediately.
Prints “start” to the console.
✅ Console:start
Step 2 — a() call
The async function
a()is invoked and added to the Call Stack.Execution starts inside
a().
Step 3 — Inside a: await b()
When
await b()runs, it calls the async functionb().a()pauses execution at theawaitpoint untilb()'s Promise resolves.The rest of
a()(console.log('a end')) is scheduled as a Microtask.
Step 4 — Inside b: console.log('b')
The function
b()executes synchronously up to completion.Prints “b” to the console.
✅ Console:start, b
✅ Microtask Queue:console.log('a end')
Step 5 — console.log('end')
The next synchronous statement executes.
Prints “end” to the console.
✅ Console:start, b, end
Step 6 — Stack Empty → Event Loop runs Microtasks
All synchronous code has finished, so the Call Stack is empty.
The Event Loop checks the Microtask Queue and finds the continuation of
a()(console.log('a end')).
Step 7 — Run Microtask (console.log('a end'))
The remaining part of
a()executes.Prints “a end” to the console.
✅ Final Console:start, b, end, a end
async function test() {
console.log(1);
await Promise.resolve();
console.log(2);
}
test();
console.log(3);
// 1
// 3
// 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | test() | — | — |
| 2 | Inside test: console.log(1) | — | — |
| ⏩ After exec | — | — | 1 |
| 3 | Inside test: await Promise.resolve() | Microtask Queue: [console.log(2)] | 1 |
| 4 | console.log(3) | Microtask Queue: [console.log(2)] | 1 |
| ⏩ After exec | — | Microtask Queue: [console.log(2)] | 1, 3 |
| 5 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log(2)) | 1, 3 |
| 6 | console.log(2) | — | 1, 3 |
| ⏩ After exec | — | — | 1, 3, 2 |
Step-by-Step Explanation
Step 1 — test() call
The async function
test()is invoked and enters the Call Stack.Execution starts inside the function body.
Step 2 — Inside test: console.log(1)
This synchronous statement runs immediately.
Prints “1” to the console.
✅ Console:1
Step 3 — await Promise.resolve()
The
Promise.resolve()returns a resolved Promise instantly.Because of the
await, the rest oftest()(the code after theawait) is paused and scheduled as a Microtask (console.log(2)).
✅ Microtask Queue:console.log(2)
Step 4 — console.log(3)
The next synchronous line after the
test()call executes.Prints “3” to the console.
✅ Console:1, 3
Step 5 — Stack Empty → Event Loop runs Microtasks
Once all synchronous code finishes, the Call Stack is empty.
The Event Loop picks the Microtask (
console.log(2)from the async continuation).
Step 6 — Run Microtask (console.log(2))
The paused part of
test()resumes and runsconsole.log(2).Prints “2” to the console.
✅ Final Console:1, 3, 2
async function x() {
console.log('x start');
await y();
console.log('x end');
}
async function y() {
console.log('y');
}
x();
console.log('end');
// x start
// y
// end
// x end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | x() | — | — |
| 2 | Inside x: console.log('x start') | — | — |
| ⏩ After exec | — | — | x start |
| 3 | Inside x: await y() → calls y() | — | x start |
| 4 | Inside y: console.log('y') | — | x start |
| ⏩ After exec | — | Microtask Queue: [console.log('x end')] | x start, y |
| 5 | console.log('end') | Microtask Queue: [console.log('x end')] | x start, y |
| ⏩ After exec | — | Microtask Queue: [console.log('x end')] | x start, y, end |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('x end')) | x start, y, end |
| 7 | console.log('x end') | — | x start, y, end |
| ⏩ After exec | — | — | x start, y, end, x end |
Step-by-Step Explanation
Step 1 — x() call
The async function
x()is invoked.It enters the Call Stack and starts executing.
Step 2 — Inside x: console.log('x start')
Synchronous code executes immediately.
Prints “x start” to the console.
✅ Console:x start
Step 3 — Inside x: await y()
Calls the async function
y().The execution of
x()pauses afterawaituntily()’s Promise resolves.The remaining code (
console.log('x end')) is queued as a Microtask.
Step 4 — Inside y: console.log('y')
Runs synchronously and prints “y”.
y()finishes and returns a resolved Promise.
✅ Console:x start, y
✅ Microtask Queue:console.log('x end')
Step 5 — console.log('end')
After
x()paused, control returns to global synchronous code.Prints “end”.
✅ Console:x start, y, end
Step 6 — Stack Empty → Event Loop runs Microtasks
Once all synchronous tasks are complete, the Event Loop checks the Microtask Queue.
Finds
console.log('x end')waiting fromx()’s continuation.
Step 7 — Run Microtask (console.log('x end'))
Executes the paused part of
x().Prints “x end”.
✅ Final Console:x start, y, end, x end
async function test() {
console.log('A');
await null;
console.log('B');
}
test();
Promise.resolve().then(() => console.log('C'));
console.log('D');
// A
// D
// B
// C
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | test() | — | — |
| 2 | Inside test: console.log('A') | — | — |
| ⏩ After exec | — | — | A |
| 3 | Inside test: await null | — | A |
| ⏩ After exec | — | Microtask Queue: [console.log('B')] | A |
| 4 | Promise.resolve().then(() => console.log('C')) | — | A |
| ⏩ After exec | — | Microtask Queue: [console.log('B'), console.log('C')] | A |
| 5 | console.log('D') | Microtask Queue: [console.log('B'), console.log('C')] | A |
| ⏩ After exec | — | Microtask Queue: [console.log('B'), console.log('C')] | A, D |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtasks | A, D |
| 7 | console.log('B') | Microtask Queue: [console.log('C')] | A, D |
| ⏩ After exec | — | Microtask Queue: [console.log('C')] | A, D, B |
| 8 | console.log('C') | — | A, D, B |
| ⏩ After exec | — | — | A, D, B, C |
Step-by-Step Explanation
Step 1 — test() call
The async function
test()is invoked.Added to the Call Stack and starts executing.
Step 2 — Inside test: console.log('A')
- Runs synchronously, so it prints “A” immediately.
✅ Console:A
Step 3 — Inside test: await null
await nullis equivalent toPromise.resolve(null)— it creates a resolved Promise.The rest of
test()(console.log('B')) is paused and scheduled as a Microtask.
✅ Microtask Queue:console.log('B')
Step 4 — Promise.resolve().then(() => console.log('C'))
- This schedules another Microtask for “C” to print later.
✅ Microtask Queue now: [console.log('B'),console.log('C')]
Step 5 — console.log('D')
Runs synchronously in the main script.
Prints “D” immediately.
✅ Console:A, D
Step 6 — Event Loop starts Microtasks
- Call Stack is empty → Event Loop picks Microtasks in FIFO order (first-in-first-out).
Step 7 — Execute Microtask: console.log('B')
- The async function
test()resumes and prints “B”.
✅ Console:A, D, B
Step 8 — Execute Microtask: console.log('C')
- The
.then()callback runs next and prints “C”.
✅ Final Console:A, D, B, C
console.log('start');
setTimeout(() => console.log('timeout1'));
Promise.resolve().then(() => console.log('p1')).then(() => console.log('p2'));
setTimeout(() => console.log('timeout2'));
console.log('end');
// start
// end
// p1
// p2
// timeout1
// timeout2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | setTimeout(() => console.log('timeout1')) | Web API Timers: [console.log('timeout1')] | start |
| 3 | Promise.resolve().then(() => console.log('p1')).then(() => console.log('p2')) | Web API Timers: [timeout1] Microtask Queue: [console.log('p1')] | start |
| 4 | setTimeout(() => console.log('timeout2')) | Web API Timers: [timeout1, timeout2] Microtask Queue: [console.log('p1')] | start |
| 5 | console.log('end') | Web API Timers: [timeout1, timeout2] Microtask Queue: [console.log('p1')] | start |
| ⏩ After exec | — | Web API Timers: [timeout1, timeout2] Microtask Queue: [console.log('p1')] | start, end |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('p1')) | start, end |
| 7 | console.log('p1') | Web API Timers: [timeout1, timeout2] Microtask Queue: [console.log('p2')] | start, end |
| ⏩ After exec | — | Web API Timers: [timeout1, timeout2] Microtask Queue: [console.log('p2')] | start, end, p1 |
| 8 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('p2')) | start, end, p1 |
| 9 | console.log('p2') | Web API Timers: [timeout1, timeout2] | start, end, p1 |
| ⏩ After exec | — | Web API Timers: [timeout1, timeout2] | start, end, p1, p2 |
| 10 | — (Stack empty) | 🟢 Event Loop picks first Macrotask (timeout1) | start, end, p1, p2 |
| 11 | console.log('timeout1') | Web API Timers: [timeout2] | start, end, p1, p2 |
| ⏩ After exec | — | Web API Timers: [timeout2] | start, end, p1, p2, timeout1 |
| 12 | — (Stack empty) | 🟢 Event Loop picks next Macrotask (timeout2) | start, end, p1, p2, timeout1 |
| 13 | console.log('timeout2') | — | start, end, p1, p2, timeout1 |
| ⏩ After exec | — | — | start, end, p1, p2, timeout1, timeout2 |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs synchronously.
Prints “start” immediately.
✅ Console:start
Step 2 — setTimeout(() => console.log('timeout1'))
Timer callback is sent to the Web API Timer queue.
Will move to the Macrotask queue after delay (0ms).
✅ Web API Timers: [timeout1]
Step 3 — Promise.resolve().then(...).then(...)
Creates a resolved Promise.
The first
.then()(console.log('p1')) goes to the Microtask Queue.
✅ Microtask Queue: [console.log('p1')]
Step 4 — setTimeout(() => console.log('timeout2'))
- Another timer callback is registered in the Web API Timer queue.
✅ Web API Timers: [timeout1,timeout2]
Step 5 — console.log('end')
Synchronous code continues.
Prints “end”.
✅ Console:start, end
Step 6 — Stack Empty → Event Loop picks Microtasks
Call Stack is now empty.
The Event Loop first executes all Microtasks before any Macrotasks.
Step 7 — Execute Microtask: console.log('p1')
Prints “p1”.
The
.then()chained to it schedules another Microtask for “p2”.
✅ Microtask Queue: [console.log('p2')]
✅ Console:start, end, p1
Step 8 — Execute Microtask: console.log('p2')
- Prints “p2”.
✅ Console:start, end, p1, p2
Step 9 — All Microtasks done → Run Macrotasks
The Event Loop now moves to Macrotask Queue (timers).
Executes the first timer:
timeout1.
✅ Console:start, end, p1, p2, timeout1
Step 10 — Next Timer (timeout2)
- Executes second timer.
✅ Final Console:start, end, p1, p2, timeout1, timeout2
async function foo() {
console.log('foo start');
await Promise.resolve();
console.log('foo end');
}
Promise.resolve().then(() => console.log('promise1'));
foo();
console.log('script');
// foo start
// script
// promise1
// foo end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | Promise.resolve().then(() => console.log('promise1')) | Microtask Queue: [console.log('promise1')] | — |
| 2 | foo() | Microtask Queue: [console.log('promise1')] | — |
| 3 | Inside foo: console.log('foo start') | Microtask Queue: [console.log('promise1')] | — |
| ⏩ After exec | — | Microtask Queue: [console.log('promise1')] | foo start |
| 4 | Inside foo: await Promise.resolve() | — | foo start |
| ⏩ After exec | — | Microtask Queue: [console.log('promise1'), console.log('foo end')] | foo start |
| 5 | console.log('script') | Microtask Queue: [console.log('promise1'), console.log('foo end')] | foo start |
| ⏩ After exec | — | Microtask Queue: [console.log('promise1'), console.log('foo end')] | foo start, script |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtasks | foo start, script |
| 7 | console.log('promise1') | Microtask Queue: [console.log('foo end')] | foo start, script |
| ⏩ After exec | — | Microtask Queue: [console.log('foo end')] | foo start, script, promise1 |
| 8 | console.log('foo end') | — | foo start, script, promise1 |
| ⏩ After exec | — | — | foo start, script, promise1, foo end |
Step-by-Step Explanation
Step 1 — Promise.resolve().then(() => console.log('promise1'))
A resolved Promise is created immediately.
The
.then()callback (console.log('promise1')) is queued in the Microtask Queue.
✅ Microtask Queue: [promise1]
Step 2 — foo() call
The async function
foo()is invoked.Added to the Call Stack and begins execution.
Step 3 — Inside foo: console.log('foo start')
Synchronous statement runs instantly.
Prints “foo start”.
✅ Console:foo start
Step 4 — Inside foo: await Promise.resolve()
Promise.resolve()returns a resolved Promise.The rest of
foo()(console.log('foo end')) is scheduled as a Microtask.
✅ Microtask Queue: [promise1,foo end]
Step 5 — console.log('script')
Global synchronous code continues.
Prints “script” immediately.
✅ Console:foo start, script
Step 6 — Stack Empty → Event Loop runs Microtasks
Call Stack is now empty.
Event Loop picks tasks from Microtask Queue in FIFO order.
Step 7 — Execute Microtask: console.log('promise1')
Runs first (it was queued before
foo end).Prints “promise1”.
✅ Console:foo start, script, promise1
Step 8 — Execute Microtask: console.log('foo end')
Resumes the paused async function
foo()afterawait.Prints “foo end”.
✅ Final Console:foo start, script, promise1, foo end
console.log('1');
Promise.resolve().then(() => console.log('2')).then(() => console.log('3'));
console.log('4');
// 1
// 4
// 2
// 3
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | console.log('1') | — | — |
| ⏩ After exec | — | — | 1 |
| 2 | Promise.resolve().then(() => console.log('2')).then(() => console.log('3')) | Microtask Queue: [console.log('2')] | 1 |
| 3 | console.log('4') | Microtask Queue: [console.log('2')] | 1 |
| ⏩ After exec | — | Microtask Queue: [console.log('2')] | 1, 4 |
| 4 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('2')) | 1, 4 |
| 5 | console.log('2') | Microtask Queue: [console.log('3')] | 1, 4 |
| ⏩ After exec | — | Microtask Queue: [console.log('3')] | 1, 4, 2 |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('3')) | 1, 4, 2 |
| 7 | console.log('3') | — | 1, 4, 2 |
| ⏩ After exec | — | — | 1, 4, 2, 3 |
Step-by-Step Explanation
Step 1 — console.log('1')
- Runs synchronously and prints “1” immediately.
✅ Console:1
Step 2 — Promise.resolve().then(...).then(...)
Creates a resolved Promise instantly.
The first
.then()callback (console.log('2')) is queued in the Microtask Queue.
✅ Microtask Queue: [console.log('2')]
Step 3 — console.log('4')
Continues synchronous execution.
Prints “4” immediately.
✅ Console:1, 4
Step 4 — Event Loop picks Microtasks
- Call Stack is empty, so the Event Loop starts executing Microtasks in order.
Step 5 — Execute Microtask: console.log('2')
Prints “2”.
The chained
.then()schedules another Microtask (console.log('3')).
✅ Microtask Queue: [console.log('3')]
✅ Console:1, 4, 2
Step 6 — Execute Microtask: console.log('3')
- Runs the next Microtask and prints “3”.
✅ Final Console:1, 4, 2, 3
async function test() {
console.log('start');
await new Promise(resolve => {
console.log('promise executor');
resolve();
});
console.log('after await');
}
test();
console.log('end');
// start
// promise executor
// end
// after await
Execution Table
| Step | Call Stack | Web APIs / Queues | Console (so far) |
| 1 | test() | — | — |
| 2 | Inside test: console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 3 | Inside test: await new Promise(...) | — | start |
| 4 | Executes Promise executor → console.log('promise executor') | — | start |
| ⏩ After exec | — | Microtask Queue: [console.log('after await')] | start, promise executor |
| 5 | console.log('end') | Microtask Queue: [console.log('after await')] | start, promise executor |
| ⏩ After exec | — | Microtask Queue: [console.log('after await')] | start, promise executor, end |
| 6 | — (Stack empty) | 🟢 Event Loop picks Microtask (console.log('after await')) | start, promise executor, end |
| 7 | console.log('after await') | — | start, promise executor, end |
| ⏩ After exec | — | — | start, promise executor, end, after await |
Step-by-Step Explanation
Step 1 — Call test()
The async function
test()is invoked.Added to the Call Stack and starts executing.
Step 2 — Inside test: console.log('start')
- Runs synchronously and prints “start”.
✅ Console:start
Step 3 — Inside test: await new Promise(...)
A new Promise is created.
Its executor function runs immediately.
Step 4 — Inside the Promise executor
Prints “promise executor”.
Calls
resolve(), which marks the Promise as resolved.The remainder of
test()(console.log('after await')) is queued as a Microtask.
✅ Microtask Queue: [console.log('after await')]
✅ Console:start, promise executor
Step 5 — console.log('end')
After
test()pauses atawait, the global code continues.Prints “end”.
✅ Console:start, promise executor, end
Step 6 — Stack Empty → Event Loop runs Microtasks
Call Stack is empty.
Event Loop checks Microtask Queue and finds
console.log('after await').
Step 7 — Execute Microtask: console.log('after await')
- Prints “after await”.
✅ Final Console:start, promise executor, end, after await
console.log('A');
Promise.resolve().then(() => {
console.log('B');
Promise.resolve().then(() => console.log('C'));
});
console.log('D');
// A
// D
// B
// C
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('A') | — | — |
| ⏩ After exec | — | — | A |
| 2 | Promise.resolve().then(...) | Microtask Queue: [then callback → console.log('B')] | A |
| 3 | console.log('D') | Microtask Queue: [then callback → console.log('B')] | A |
| ⏩ After exec | — | Microtask Queue: [then callback → console.log('B')] | A, D |
| 4 | — (Stack empty) | 🟢 Event Loop executes first Microtask (console.log('B')) | A, D |
| 5 | Inside Microtask: console.log('B') | Adds new Microtask [console.log('C')] | A, D |
| 6 | — | Microtask Queue: [console.log('C')] | A, D, B |
| 7 | — (Stack empty again) | 🟢 Event Loop executes next Microtask (console.log('C')) | A, D, B |
| ⏩ After exec | — | — | A, D, B, C |
Step-by-Step Explanation
Step 1 — console.log('A')
- Runs synchronously and prints “A”.
✅ Console:A
Step 2 — Promise.resolve().then(...)
Creates a resolved promise.
The
.then()callback (() => { console.log('B'); Promise.resolve().then(...); })
is scheduled in the Microtask Queue.
✅ Microtask Queue:[B callback]
Step 3 — console.log('D')
- Executes synchronously after the Promise setup.
✅ Console:A, D
Step 4 — Global execution done → Event Loop runs Microtasks
Call Stack is now empty.
Event Loop picks the first Microtask: the one that logs “B”.
Step 5 — Execute Microtask: console.log('B')
Prints “B”.
Inside this callback, another
Promise.resolve().then(...)adds a new Microtask for “C”.
✅ Console:A, D, B
✅ Microtask Queue:[C callback]
Step 6 — Next Microtask: console.log('C')
- Event Loop executes this second Microtask.
✅ Final Console:A, D, B, C
console.log('start');
setTimeout(() => console.log('timeout'));
Promise.resolve().then(() => {
console.log('promise1');
setTimeout(() => console.log('inner timeout'));
});
console.log('end');
// start
// end
// promise1
// timeout
// inner timeout
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | setTimeout(...) | Web API Timer: [console.log('timeout')] | start |
| 3 | Promise.resolve().then(...) | Web API Timer: [console.log('timeout')]Microtask Queue: [promise1 callback] | start |
| 4 | console.log('end') | Web API Timer: [console.log('timeout')]Microtask Queue: [promise1 callback] | start |
| ⏩ After exec | — | Web API Timer: [console.log('timeout')]Microtask Queue: [promise1 callback] | start, end |
| 5 | — (Stack empty) | 🟢 Event Loop executes Microtask (promise1) | start, end |
| 6 | console.log('promise1') | Adds new timer → Web API Timer: [console.log('timeout'), console.log('inner timeout')] | start, end, promise1 |
| 7 | — (Stack empty) | 🕓 Event Loop executes Macrotasks in order: first timeout, then inner timeout | start, end, promise1 |
| 8 | console.log('timeout') | Web API Timer: [console.log('inner timeout')] | start, end, promise1, timeout |
| 9 | console.log('inner timeout') | — | start, end, promise1, timeout, inner timeout |
Step-by-Step Explanation
Step 1 — console.log('start')
- Runs synchronously and prints “start”.
✅ Console:start
Step 2 — setTimeout(() => console.log('timeout'))
- The callback is sent to the Web API Timer to run later as a macrotask.
✅ Web API Timer:[timeout callback]
✅ Console:start
Step 3 — Promise.resolve().then(...)
- The
.then()callback (() => { console.log('promise1'); setTimeout(...); })
is queued in the Microtask Queue.
✅ Microtask Queue:[promise1 callback]
Step 4 — console.log('end')
- Executes synchronously and prints “end”.
✅ Console:start, end
Step 5 — Event Loop runs Microtasks first
The Call Stack is empty → Event Loop picks the Microtask.
Executes
console.log('promise1'), printing “promise1”.Inside this callback, another
setTimeout(() => console.log('inner timeout'))
is registered → added to the Web API Timer.
✅ Web API Timer:[timeout, inner timeout]
✅ Console:start, end, promise1
Step 6 — Event Loop moves to Macrotasks
- Microtasks done → next, the Macrotask Queue runs timers in order of registration.
Executes first timer → “timeout”
Executes next timer → “inner timeout”
✅ Final Console: start, end, promise1, timeout, inner timeout
async function run() {
console.log('1');
await Promise.resolve();
console.log('2');
}
run();
run();
console.log('3');
// 1
// 1
// 3
// 2
// 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (Function definitions loaded) | — | — |
| 2 | run() (1st call) → console.log('1') | — | 1 |
| 3 | await Promise.resolve() (1st run) | Microtask Queue: [resume run() → console.log('2')] | 1 |
| 4 | run() (2nd call) → console.log('1') | Microtask Queue: [run1→2, run2→2] | 1, 1 |
| 5 | await Promise.resolve() (2nd run) | Microtask Queue: [resume run1, resume run2] | 1, 1 |
| 6 | console.log('3') | Microtask Queue: [resume run1, resume run2] | 1, 1, 3 |
| 7 | — (Stack empty) | 🟢 Event Loop executes Microtasks → runs run() continuations | 1, 1, 3 |
| 8 | Executes console.log('2') (from first run) | Remaining Microtask: [resume run2] | 1, 1, 3, 2 |
| 9 | Executes console.log('2') (from second run) | — | 1, 1, 3, 2, 2 |
Step-by-Step Explanation
Step 1 — Function definition phase
- The
async function run()is defined but not executed yet.
Step 2 — First run() call
Executes synchronously up to the first
await.Prints “1”.
The
await Promise.resolve()pauses the function and schedules its continuation (the part after theawait) into the Microtask Queue.
✅ Microtask Queue:[run1 continuation → console.log('2')]
✅ Console:1
Step 3 — Second run() call
Executes independently of the first.
Prints another “1”.
Again, the
awaitpauses execution and schedules the next part in the Microtask Queue.
✅ Microtask Queue:[run1→2, run2→2]
✅ Console:1, 1
Step 4 — console.log('3')
- This line is synchronous and runs immediately after both
run()calls are initiated.
✅ Console:1, 1, 3
Step 5 — Event Loop runs Microtasks
After all synchronous code is done, the Event Loop picks Microtasks in order.
First continuation (
run1) executes → prints “2”.Then second continuation (
run2) executes → prints “2” again.
✅ Final Console:1, 1, 3, 2, 2
async function run() {
console.log('run');
return Promise.resolve('done');
}
run().then(res => console.log(res));
console.log('after');
// run
// after
// done
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (Function definition loaded) | — | — |
| 2 | run() → console.log('run') | — | run |
| 3 | return Promise.resolve('done') | Microtask Queue: [then callback → console.log(res)] | run |
| 4 | console.log('after') | Microtask Queue: [then callback → console.log(res)] | run, after |
| 5 | — (Stack empty) | 🟢 Event Loop executes Microtask | run, after |
| 6 | Executes .then(res => console.log(res)) | — | run, after, done |
Step-by-Step Explanation
Step 1 — Function definition
- The
async function run()is defined but not yet executed.
Step 2 — Call run()
The function
run()executes immediately.Prints “run”.
Returns a resolved Promise with the value
'done'.Since
run()isasync, it automatically wraps the returned value in a Promise.
✅ Console:run
✅ Microtask Queue:[.then() callback waiting for 'done']
Step 3 — console.log('after')
- Executes synchronously right after calling
run().
✅ Console:run, after
Step 4 — Event Loop phase
The Call Stack is now empty, so the Event Loop moves to the Microtask Queue.
It executes the
.then()callback attached to the resolved Promise.The callback prints “done”.
✅ Final Console Output: run, after, done
console.log('start');
Promise.resolve().then(() => console.log('p1'));
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('p2'));
console.log('end');
// start
// end
// p1
// p2
// timeout
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | Promise.resolve().then(() => console.log('p1')) | Microtask Queue: [p1] | start |
| 3 | setTimeout(() => console.log('timeout'), 0) | Web API Timer: [timeout] | start |
| 4 | Promise.resolve().then(() => console.log('p2')) | Web API Timer: [timeout]Microtask Queue: [p1, p2] | start |
| 5 | console.log('end') | Web API Timer: [timeout]Microtask Queue: [p1, p2] | start |
| ⏩ After exec | — | Web API Timer: [timeout]Microtask Queue: [p1, p2] | start, end |
| 6 | — (Stack empty) | 🟢 Event Loop executes Microtasks (p1, then p2) | start, end |
| 7 | console.log('p1') | Web API Timer: [timeout]Microtask Queue: [p2] | start, end, p1 |
| 8 | console.log('p2') | Web API Timer: [timeout] | start, end, p1, p2 |
| 9 | — (Stack empty again) | 🕓 Event Loop executes Macrotask → timeout | start, end, p1, p2 |
| 10 | console.log('timeout') | — | start, end, p1, p2, timeout |
Step-by-Step Explanation
Step 1 — console.log('start')
- Runs synchronously and prints “start”.
✅ Console:start
Step 2 — Promise.resolve().then(() => console.log('p1'))
- A resolved Promise immediately schedules its
.then()callback in the Microtask Queue.
✅ Microtask Queue:[p1]
Step 3 — setTimeout(..., 0)
- The callback is sent to the Web API Timer to be executed later as a macrotask.
✅ Web API Timer:[timeout]
Step 4 — Second Promise (p2)
- Another resolved Promise adds its
.then()callback to the Microtask Queue afterp1.
✅ Microtask Queue:[p1, p2]
Step 5 — console.log('end')
- Runs synchronously and prints “end”.
✅ Console:start, end
Step 6 — Event Loop runs Microtasks first
- The Call Stack is empty, so the Event Loop executes all Microtasks in order.
Runs
p1→ prints “p1”Runs
p2→ prints “p2”
✅ Console:start, end, p1, p2
Step 7 — Event Loop moves to Macrotasks
After all Microtasks are done, the Event Loop picks up the Macrotask from the timer queue.
Executes
console.log('timeout'), printing “timeout”.
✅ Final Console:start, end, p1, p2, timeout
async function one() {
console.log('1');
await two();
console.log('2');
}
async function two() {
console.log('3');
await null;
console.log('4');
}
console.log('start');
one();
console.log('end');
// start
// 1
// 3
// end
// 4
// 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (Functions defined) | — | — |
| 2 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 3 | one() | — | start |
| 4 | Inside one(): console.log('1') | — | start, 1 |
| 5 | await two() | — | start, 1 |
| 6 | Inside two(): console.log('3') | — | start, 1, 3 |
| 7 | await null (in two) | Microtask Queue: [resume two → console.log('4')] | start, 1, 3 |
| 8 | one() paused (waiting for two() to finish) | Microtask Queue: [resume two → console.log('4')] | start, 1, 3 |
| 9 | console.log('end') | Microtask Queue: [resume two → console.log('4')] | start, 1, 3, end |
| 10 | — (Stack empty) | 🟢 Event Loop executes Microtask: console.log('4') | start, 1, 3, end |
| 11 | After two() finishes → resumes one() | Microtask Queue: [resume one → console.log('2')] | start, 1, 3, end, 4 |
| 12 | 🟢 Event Loop executes next Microtask: console.log('2') | — | start, 1, 3, end, 4, 2 |
Step-by-Step Explanation
Step 1 — Function definitions
one()andtwo()are defined but not executed yet.
Step 2 — console.log('start')
- Runs synchronously and prints “start”.
✅ Console:start
Step 3 — Call one()
Enters the
asyncfunctionone().Prints “1”.
✅ Console:start, 1
Step 4 — await two()
- Before pausing, it calls
two().
Step 5 — Inside two()
Prints “3” immediately.
✅ Console:start, 1, 3Encounters
await null, which pausestwo()and schedules continuation (console.log('4')) in the Microtask Queue.
✅ Microtask Queue:[two continuation → console.log('4')]
Step 6 — Back to one()
one()now pauses (waiting fortwo()’s result).
✅ Microtask Queue:[two continuation]
Step 7 — console.log('end')
- Runs synchronously after
one()is paused.
✅ Console:start, 1, 3, end
Step 8 — Event Loop → Run Microtasks
First microtask resumes
two()→ prints “4”.
✅ Console:start, 1, 3, end, 4Once
two()finishes, its returned Promise resolves, soone()’sawait two()continues.
✅ New Microtask:[one continuation → console.log('2')]
Step 9 — Run next Microtask
Resumes
one()afterawait two().Prints “2”.
✅ Console:start, 1, 3, end, 4, 2
async function foo() {
console.log('foo start');
await Promise.resolve();
console.log('foo mid');
await Promise.resolve();
console.log('foo end');
}
console.log('script');
foo();
console.log('after');
// script
// foo start
// after
// foo mid
// foo end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (Function defined) | — | — |
| 2 | console.log('script') | — | — |
| ⏩ After exec | — | — | script |
| 3 | foo() | — | script |
| 4 | Inside foo: console.log('foo start') | — | script, foo start |
| 5 | await Promise.resolve() (1st await) | Microtask Queue: [resume foo → console.log('foo mid')] | script, foo start |
| 6 | console.log('after') | Microtask Queue: [resume foo → console.log('foo mid')] | script, foo start, after |
| 7 | — (Stack empty) | 🟢 Event Loop runs Microtask (resume foo → console.log('foo mid')) | script, foo start, after |
| 8 | Inside resumed foo: console.log('foo mid') | Schedules next continuation → [resume foo → console.log('foo end')] | script, foo start, after, foo mid |
| 9 | — (Stack empty again) | 🟢 Event Loop runs next Microtask (resume foo → console.log('foo end')) | script, foo start, after, foo mid |
| 10 | console.log('foo end') | — | script, foo start, after, foo mid, foo end |
Step-by-Step Explanation
Step 1 — Function definition
async function foo()is declared but not executed yet.
Step 2 — console.log('script')
- Runs synchronously and prints “script”.
✅ Console:script
Step 3 — Call foo()
Execution enters the
foo()function.Prints “foo start” synchronously.
✅ Console:script, foo start
Step 4 — First await Promise.resolve()
- The Promise is already resolved, so the continuation (next line after the
await)
is scheduled in the Microtask Queue.
✅ Microtask Queue:[resume foo → console.log('foo mid')]
Step 5 — console.log('after')
The synchronous part of the script continues.
Prints “after”.
✅ Console:script, foo start, after
Step 6 — Event Loop runs Microtask
The Call Stack is now empty.
The Event Loop runs the first microtask, resuming
foo()after the firstawait.Prints “foo mid”.
✅ Console:script, foo start, after, foo mid
Step 7 — Second await Promise.resolve()
Again, the Promise is already resolved.
The next part (
console.log('foo end')) is queued as a new Microtask.
✅ Microtask Queue:[resume foo → console.log('foo end')]
Step 8 — Run next Microtask
Event Loop picks the next microtask, resuming
foo()again.Prints “foo end”.
✅ Console:script, foo start, after, foo mid, foo end
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve();
}).then(() => {
console.log('p2');
return Promise.resolve();
}).then(() => console.log('p3'));
console.log('script');
// script
// p1
// p2
// p3
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (Promise chain registered) | — | — |
| 2 | console.log('script') | — | script |
| 3 | (Stack empty → microtasks run) | Microtask Queue: [then → console.log('p1')] | script |
| 4 | console.log('p1') executes → schedules next .then() | Microtask Queue: [then → console.log('p2')] | script, p1 |
| 5 | Event Loop runs next microtask → console.log('p2') | Microtask Queue: [then → console.log('p3')] | script, p1, p2 |
| 6 | Event Loop runs next microtask → console.log('p3') | — | script, p1, p2, p3 |
Step-by-Step Explanation
Step 1 — Promise chain registration
The
Promise.resolve()chain is created.All
.then()callbacks are stored to run after the current synchronous code finishes.No output yet.
Step 2 — console.log('script')
Runs synchronously.
Prints “script”.
✅ Console:script
Step 3 — Microtask phase begins
Main thread is now free.
Event loop starts processing microtasks (promise callbacks).
Step 4 — First .then() runs
Executes
console.log('p1').
✅ Console:script, p1The
.then()returns another resolved Promise.This queues the next
.then()→console.log('p2').
Step 5 — Second .then() runs
Executes
console.log('p2').
✅ Console:script, p1, p2Again returns a resolved Promise → queues next
.then()→console.log('p3').
Step 6 — Third .then() runs
- Executes
console.log('p3').
✅ Console:script, p1, p2, p3
async function first() {
console.log('1');
await second();
console.log('2');
}
async function second() {
console.log('3');
await Promise.resolve();
console.log('4');
}
setTimeout(() => console.log('5'));
first();
console.log('6');
// 1
// 3
// 6
// 4
// 2
// 5
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (register functions + setTimeout callback) | Macrotask Queue: [timeout → 5] | — |
| 2 | first() | — | — |
| 3 | console.log('1') | — | 1 |
| 4 | second() | — | — |
| 5 | console.log('3') | — | 1, 3 |
| 6 | await Promise.resolve() → pauses second() | Microtask Queue: resume second() | 1, 3 |
| 7 | console.log('6') | — | 1, 3, 6 |
| 8 | Microtask: resume second() → console.log('4') | — | 1, 3, 6, 4 |
| 9 | Microtask: resume first() → console.log('2') | — | 1, 3, 6, 4, 2 |
| 10 | Macrotask: setTimeout → console.log('5') | — | 1, 3, 6, 4, 2, 5 |
Step-by-Step Explanation
Step 1 — Setup phase
The two async functions are defined.
setTimeout(() => console.log('5'))is scheduled.Its callback goes into the Macrotask Queue.
No output yet.
Step 2 — first() is called
- Enters call stack.
Step 3 — Inside first(): console.log('1')
- Prints 1.
✅Console:1
Step 4 — Inside first(): await second()
Calls
second().Execution enters
second().
Step 5 — Inside second(): console.log('3')
- Prints 3.
✅ Console:1, 3
Step 6 — Inside second(): await Promise.resolve()
The
awaitpausessecond()here.A microtask is scheduled to resume
second().
Step 7 — Back to main script: console.log('6')
Since async functions paused, synchronous code continues.
Prints 6.
✅ Console:1, 3, 6
Step 8 — Microtask: resume second()
Runs the rest of
second().Prints 4.
✅ Console:1, 3, 6, 4After finishing
second(), the awaiting part offirst()is now queued as a microtask.
Step 9 — Microtask: resume first()
Executes the remaining lines of
first().Prints 2.
✅ Console:1, 3, 6, 4, 2
Step 10 — Macrotask Queue runs
setTimeoutcallback finally executes.Prints 5.
✅ Console:1, 3, 6, 4, 2, 5
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => console.log('qm1'));
});
queueMicrotask(() => console.log('qm0'));
console.log('end');
// start
// end
// p1
// qm0
// qm1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console |
| 1 | console.log('start') | — | — |
| ⏩ After exec | — | — | start |
| 2 | Promise.resolve().then() | Microtask Queue: [p1 → qm1] | start |
| 3 | queueMicrotask(qm0) | Microtask Queue: [p1 → qm1, qm0] | start |
| 4 | console.log('end') | Microtask Queue: [p1 → qm1, qm0] | start |
| ⏩ After exec | — | Microtask Queue: [p1 → qm1, qm0] | start, end |
| 5 | Microtask: p1 | Microtask Queue: [qm1, qm0] | start, end |
| 6 | Microtask: qm0 | Microtask Queue: [qm1] | start, end, p1 |
| 7 | Microtask: qm1 | — | p1, qm0 |
Step-by-Step Explanation
Step 1 — console.log('start')
Synchronous.
Runs immediately.
Prints
start.
✅ Console: start
Step 2 — Promise.resolve().then(...)
The
.then()callback is scheduled into the Microtask Queue.Inside it,
queueMicrotask(qm1)is also scheduled but will only be queued after p1 runs, not now.
Step 3 — queueMicrotask(qm0)
This microtask (
qm0) is added directly to the Microtask Queue.Queue order now: p1 → qm1 (pending inside p1), qm0.
Step 4 — console.log('end')
Synchronous.
Executes immediately.
✅ Console: start, end
Step 5 — Microtask: p1
Runs first microtask.
Prints
p1.While running, it schedules another microtask: qm1 (queued at the end).
Queue now: qm0 → qm1
Step 6 — Microtask: qm0
Runs next.
Prints
qm0.
Step 7 — Microtask: qm1
Runs last.
Prints
qm1.
async function a() {
console.log('a1');
await Promise.resolve();
console.log('a2');
}
async function b() {
console.log('b1');
await null;
console.log('b2');
}
a();
b();
console.log('sync');
// a1
// b1
// sync
// a2
// b2
Execution table
| Step | Call Stack | Web APIs / Queues | Console |
| 1 | a() → console.log('a1') | — | a1 |
| 2 | await Promise.resolve() | Microtask Queue: [a2] | a1 |
| 3 | b() → console.log('b1') | Microtask Queue: [a2] | a1, b1 |
| 4 | await null | Microtask Queue: [a2, b2] | a1, b1 |
| 5 | console.log('sync') | Microtask Queue: [a2, b2] | a1, b1, sync |
| 6 | Microtask: a2 | Microtask Queue: [b2] | a1, b1, sync |
| 7 | Microtask: b2 | — | a1, b1, sync |
Step-by-Step Explanation
Step 1 — a() starts
a1is printed.await Promise.resolve()queues the continuation (a2) into the microtask queue.
Step 2 — b() starts
b1is printed.await nullalso queues its continuation (b2) into the microtask queue.
Step 3 — console.log('sync')
Synchronous.
Executes immediately.
Step 4 — Microtask: continuation of a()
- Runs
console.log('a2').
Step 5 — Microtask: continuation of b()
- Runs
console.log('b2').
console.log('s');
setTimeout(() => console.log('t1'), 0);
Promise.resolve().then(() => {
console.log('p1');
setTimeout(() => console.log('t2'), 0);
});
console.log('e');
// s
// e
// p1
// t1
// t2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | setTimeout(t1) | Web API Timer: [t1] | s |
| 3 | Promise .then() registered | Microtask Queue: [p1 → schedule t2] | s |
| 4 | console.log('e') | Microtask Queue: [p1 → schedule t2] | s, e |
| 5 | Microtask runs: p1 | Web API Timer: [t1, t2] | s, e, p1 |
| 6 | Macrotask: t1 runs | Web API Timer: [t2] | s, e, p1, t1 |
| 7 | Macrotask: t2 runs | — | s, e, p1, t1, t2 |
Step-by-Step Explanation
Step 1 — console.log('s')
The first synchronous line runs.
It prints "s" to the console.
The call stack becomes empty after printing.
Console: s
Step 2 — setTimeout(t1)
setTimeoutis added to the call stack.Its callback
t1is moved to the Web API Timer.It will later enter the macrotask queue.
Console: s
Step 3 — Promise .then() registered
The
.then()callback is placed into the microtask queue.It will run after all synchronous code finishes.
Console: s
Step 4 — console.log('e')
Synchronous code prints "e".
After this step, the call stack becomes empty and microtasks can start.
Console: s, e
Step 5 — Microtask: p1
The microtask callback runs and prints "p1".
Inside it, another
setTimeoutschedulest2in the Web API Timer.
Console: s, e, p1
Step 6 — Macrotask: t1
The event loop moves to macrotasks.
t1executes and prints "t1".
Console: s, e, p1, t1
Step 7 — Macrotask: t2
- The next macrotask,
t2, executes and prints "t2".
Console: s, e, p1, t1, t2
async function f() {
console.log('f start');
await Promise.resolve().then(() => console.log('inner'));
console.log('f end');
}
f();
Promise.resolve().then(() => console.log('p after'));
// f start
// inner
// p after
// f end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | f() → console.log('f start') | — | f start |
| 2 | await Promise.resolve().then(inner) | Microtask Queue: [inner, f end] | f start |
| 3 | Promise .then(p after) registered | Microtask Queue: [inner, f end, p after] | f start |
| 4 | Microtask runs: inner | Microtask Queue: [f end, p after] | f start, inner |
| 5 | Microtask runs: continuation of f() → console.log('f end') | Microtask Queue: [p after] | f start, inner, f end |
| 6 | Microtask runs: p after | — | f start, inner, f end, p after |
Step-by-Step Explanation
Step 1 — f() starts
The async function
fis called.It prints "f start".
Execution reaches the
await.
Console: f start
Step 2 — await Promise.resolve().then(inner)
The
.then(() => console.log('inner'))schedules "inner" as a microtask.The continuation of
f()(which prints "f end") is also queued as a microtask.
Console: f start
Step 3 — Independent Promise .then(p after)
Another
.then()registers "p after" as a microtask.It is added after the already queued microtasks.
Console: f start
Step 4 — Microtask: inner
The first microtask runs.
Prints "inner".
Console: f start, inner
Step 5 — Microtask: continuation of f() → f end
The continuation of the async function runs.
Prints "f end".
Console: f start, inner, f end
Step 6 — Microtask: p after
The final microtask runs.
Prints "p after".
Console: f start, inner, f end, p after
console.log('1');
Promise.resolve().then(() => console.log('2'));
Promise.resolve().then(() => {
console.log('3');
return Promise.resolve().then(() => console.log('4'));
});
console.log('5');
// 1
// 5
// 2
// 3
// 4
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('1') | — | 1 |
| 2 | First Promise .then(2) registered | Microtask Queue: [2] | 1 |
| 3 | Second Promise .then(3 → then 4) registered | Microtask Queue: [2, 3→4] | 1 |
| 4 | console.log('5') | Microtask Queue: [2, 3→4] | 1, 5 |
| 5 | Microtask runs: 2 | Microtask Queue: [3→4] | 1, 5, 2 |
| 6 | Microtask runs: 3 → schedules next microtask 4 | Microtask Queue: [4] | 1, 5, 2, 3 |
| 7 | Microtask runs: 4 | — | 1, 5, 2, 3, 4 |
Step-by-Step Explanation
Step 1 — console.log('1')
Synchronous line runs immediately.
Prints 1.
Console: 1
Step 2 — First Promise .then(() => console.log('2'))
Registers a microtask for printing 2.
It will run after all synchronous code completes.
Console: 1
Step 3 — Second Promise .then(...)
Registers the microtask that prints 3.
Inside its callback, it returns another resolved promise which will later schedule 4.
Console: 1
Step 4 — console.log('5')
Synchronous.
Prints 5.
Console: 1, 5
Step 5 — Microtask: print 2
First microtask runs.
Prints 2.
Console: 1, 5, 2
Step 6 — Microtask: print 3 and schedule 4
Prints 3.
The returned promise queues another microtask for 4.
Console: 1, 5, 2, 3
Step 7 — Microtask: print 4
Final microtask runs.
Prints 4.
Console: 1, 5, 2, 3, 4
setTimeout(() => console.log('t'), 0);
queueMicrotask(() => console.log('qm'));
Promise.resolve().then(() => console.log('p'));
console.log('s');
// s
// qm
// p
// t
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (register callbacks) | Macrotask: [setTimeout → t] • Microtask: [queueMicrotask → qm], [promise.then → p] | — |
| 2 | console.log('s') | — | s |
| 3 | (stack empty → microtasks run) | Microtask Queue: [qm, p] | s |
| 4 | queueMicrotask executes → console.log('qm') | Microtask Queue: [p] | s, qm |
| 5 | promise.then executes → console.log('p') | — | s, qm, p |
| 6 | Macrotask executes → setTimeout → console.log('t') | — | s, qm, p, t |
Step-by-Step Explanation
Step 1 — Setup phase
setTimeout is registered → its callback goes to the Macrotask Queue.
queueMicrotask registers qm in the Microtask Queue.
Promise.resolve().then() registers p in the Microtask Queue.
Step 2 — console.log('s')
- Synchronous code runs first.
Console: s
Step 3 — Microtask phase begins
Call stack is empty → event loop starts running microtasks.
Current microtask queue order: qm → p.
Step 4 — Microtask: queueMicrotask runs
- Executes console.log('qm').
Console: s, qm
Step 5 — Microtask: promise.then runs
- Executes console.log('p').
Console: s, qm, p
Step 6 — Macrotask phase
The setTimeout callback runs now.
Executes console.log('t').
Console: s, qm, p, t
(async () => {
console.log('I');
await 0;
console.log('II');
})();
Promise.resolve().then(() => console.log('P'));
console.log('S');
// I
// S
// II
// P
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (register async IIFE + promise.then) | Microtask Queue: [resume async IIFE → II, promise.then → P] | — |
| 2 | console.log('I') | — | I |
| 3 | await 0 → async IIFE pauses | Microtask Queue: [resume IIFE → II, promise.then → P] | I |
| 4 | console.log('S') | — | I, S |
| 5 | Microtask: resume async IIFE → console.log('II') | Microtask Queue: [promise.then → P] | I, S, II |
| 6 | Microtask: promise.then → console.log('P') | — | I, S, II, P |
Step-by-Step Explanation
Step 1 — Setup phase
The async IIFE is created and immediately scheduled to run.
Promise.resolve().then registers a microtask for printing P.
Step 2 — Inside IIFE: console.log('I')
- First synchronous line inside the async function executes.
Console: I
Step 3 — await 0
await 0 immediately resolves.
The async function pauses, and a microtask is queued to resume it later.
Console: I
Step 4 — console.log('S')
Back to main script → synchronous code continues.
Console: I, S
Step 5 — Microtask: resume async IIFE
Async function continues and executes console.log('II').
Console: I, S, II
Step 6 — Microtask: Promise.then
Executes console.log('P').
Console: I, S, II, P
console.log('start');
Promise.resolve().then(() => console.log('p1')).then(() => console.log('p2'));
(async () => {
await null;
console.log('after await');
})();
console.log('end');
// start
// end
// p1
// after await
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | (async IIFE) call | — | start |
| 4 | await null suspends async | Microtask queued: resume → after await | start |
| 5 | console.log('end') | Microtasks: p1, then p2, then after await | start, end |
| 6 | (microtask) p1 | Microtask queued: p2 | start, end, p1 |
| 7 | (microtask) p2 | — | start, end, p1, p2 |
| 8 | (microtask) after await | — | start, end, p1, p2, after await |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
No tasks scheduled.
✅ Console: start
Step 2 — Promise.resolve().then(() => console.log('p1'))
The
.then()registers a microtask.p1is added to the Microtask Queue.
Step 3 — Async IIFE runs
(async () => { ... })() is called synchronously.
Step 4 — Inside async: await null
await nullpauses the function.Schedules a microtask to resume the async function.
That microtask will later run
console.log('after await').
Step 5 — console.log('end')
Last synchronous line.
Prints end.
✅ Console: start, end
Step 6 — Event Loop → Run Microtasks
Microtasks run in order:
Step 7 — Microtask: p1
console.log('p1')runs.Its
.then(() => console.log('p2'))schedules another microtaskp2.
✅ Console: start, end, p1
Step 8 — Microtask: p2
- Prints p2.
✅ Console: start, end, p1, p2
Step 9 — Microtask: after await
Async function resumes.
Prints after await.
✅ Final Console:start, end, p1, p2, after await
async function a() {
console.log('A1');
await Promise.resolve();
console.log('A2');
}
console.log('start');
a();
Promise.resolve().then(() => console.log('P1'));
queueMicrotask(() => console.log('QM1'));
console.log('end');
// start
// A1
// end
// A2
// P1
// QM1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | a() | — | start |
| 3 | Inside a: console.log('A1') | — | start, A1 |
| 4 | Inside a: await Promise.resolve() | Microtask: resume a() → A2 | start, A1 |
| 5 | Promise.resolve().then(... ) | Microtask queued: P1 | start, A1 |
| 6 | queueMicrotask(...) | Microtask queued: QM1 | start, A1 |
| 7 | console.log('end') | Microtasks pending: A2, P1, QM1 | start, A1, end |
| 8 | (microtask) resume async → A2 | Remaining: P1, QM1 | start, A1, end, A2 |
| 9 | (microtask) P1 | Remaining: QM1 | start, A1, end, A2, P1 |
| 10 | (microtask) QM1 | — | start, A1, end, A2, P1, QM1 |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — a()
Async function
a()is called.Execution enters the function.
Step 3 — Inside a: console.log('A1')
Runs synchronously.
Prints A1.
✅ Console: start, A1
Step 4 — await Promise.resolve()
Execution inside
a()pauses.The continuation (
console.log('A2')) is added to the Microtask Queue.
Step 5 — Promise.resolve().then(() => console.log('P1'))
- Schedules microtask P1.
Step 6 — queueMicrotask(() => console.log('QM1'))
- Schedules microtask QM1.
Step 7 — console.log('end')
Runs synchronously.
Prints end.
✅ Console: start, A1, end
Step 8 — Microtask: resume a() → console.log('A2')
Async function continues after
await.Prints A2.
✅ Console: start, A1, end, A2
Step 9 — Microtask: P1
Runs next.
Prints P1.
✅ Console: start, A1, end, A2, P1
Step 10 — Microtask: QM1
- Final microtask prints QM1.
✅ Final Console: start, A1, end, A2, P1, QM1
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => console.log('qm1'));
}).then(() => console.log('p2'));
console.log('e');
// s
// e
// p1
// qm1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | s |
| 3 | console.log('e') | Microtasks pending: p1 | s, e |
| 4 | (microtask) run p1 | queueMicrotask: qm1 added | s, e, p1 |
| 5 | After p1 resolves → .then(() => console.log('p2')) | Microtask queued: p2 | s, e, p1 |
| 6 | (microtask) run p2 | Remaining: qm1 | s, e, p1, p2 |
| 7 | (microtask) run qm1 | — | s, e, p1, p2, qm1 |
Step-by-Step Explanation
Step 1 — console.log('s')
Runs immediately.
Prints s.
✅ Console: s
Step 2 — Promise.resolve().then(...)
.then()registers a microtask that will print p1.Microtask p1 is queued.
Step 3 — console.log('e')
Executes synchronously.
Prints e.
✅ Console: s, e
Step 4 — Microtask: run p1
Microtask prints p1.
Inside
p1,queueMicrotask(() => console.log('qm1'))schedules qm1.
✅ Console: s, e, p1
Step 5 — Chain .then(() => console.log('p2'))
- After
p1finishes, its.then()schedules another microtask: p2.
Step 6 — Microtask: run p2
- Prints p2.
✅ Console: s, e, p1, p2
Step 7 — Microtask: run qm1
- Final microtask prints qm1.
✅ Final Console: s, e, p1, p2, qm1
async function x() {
console.log('x1');
await new Promise(res => res());
console.log('x2');
}
x();
Promise.resolve().then(() => console.log('p'));
console.log('sync');
// x1
// sync
// x2
// p
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | x() | — | — |
| 2 | Inside x: console.log('x1') | — | x1 |
| 3 | Inside x: await new Promise(res => res()) | Microtask queued: resume x() → run x2 | x1 |
| 4 | Promise.resolve().then(...) | Microtask queued: p | x1 |
| 5 | console.log('sync') | Microtasks pending: x2, p | x1, sync |
| 6 | (microtask) resume async → x2 | Remaining: p | x1, sync, x2 |
| 7 | (microtask) p | — | x1, sync, x2, p |
Step-by-Step Explanation
Step 1 — x()
Async function
x()is called.Execution enters the function.
Step 2 — Inside x: console.log('x1')
Runs immediately.
Prints x1.
✅ Console: x1
Step 3 — await new Promise(res => res())
new Promise(res => res())resolves instantly.awaitpausesx()and schedules a microtask that will later runconsole.log('x2').
Step 4 — Promise.resolve().then(() => console.log('p'))
- Schedules microtask p.
Step 5 — console.log('sync')
Runs synchronously.
Prints sync.
✅ Console: x1, sync
Step 6 — Microtask: resume async → console.log('x2')
x()continues afterawait.Prints x2.
✅ Console: x1, sync, x2
Step 7 — Microtask: p
Runs next.
Prints p.
✅ Final Console: x1, sync, x2, p
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve();
}).then(() => {
console.log('p2');
});
setTimeout(() => console.log('t'), 0);
console.log('end');
// start
// end
// p1
// p2
// t
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | setTimeout(..., 0) | Macrotask queued: t | start |
| 4 | console.log('end') | Microtasks pending: p1 | start, end |
| 5 | (microtask) run p1 | Microtask queued: p2 | start, end, p1 |
| 6 | (microtask) run p2 | — | start, end, p1, p2 |
| 7 | (macrotask) run t | — | start, end, p1, p2, t |
Step-by-Step Explanation
Step 1 — console.log('start')
Executes immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(...)
.then()schedules a microtask that will runp1.
Step 3 — setTimeout(() => console.log('t'), 0)
- Adds t to the macrotask (callback) queue.
Step 4 — console.log('end')
Executes synchronously.
Prints end.
✅ Console: start, end
Step 5 — Microtask: run p1
Prints p1.
The
return Promise.resolve()triggers another.then()which schedules a microtask p2.
✅ Console: start, end, p1
Step 6 — Microtask: run p2
- Prints p2.
✅ Console: start, end, p1, p2
Step 7 — Macrotask: run t
Event loop moves to macrotasks.
Prints t.
✅ Final Console: start, end, p1, p2, t
(async function() {
console.log('A');
await Promise.resolve();
console.log('B');
await null;
console.log('C');
})();
console.log('D');
// A
// D
// B
// C
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (async IIFE) starts | — | — |
| 2 | console.log('A') | — | A |
| 3 | await Promise.resolve() | Microtask queued: resume → B | A |
| 4 | console.log('D') | Microtasks pending: B | A, D |
| 5 | (microtask) resume async → console.log('B') | Microtask queued: continue → C (from await null) | A, D, B |
| 6 | (microtask) resume async → console.log('C') | — | A, D, B, C |
Step-by-Step Explanation
Step 1 — Async IIFE begins
- The immediately invoked async function starts executing.
Step 2 — console.log('A')
Executes synchronously.
Prints A.
✅ Console: A
Step 3 — await Promise.resolve()
Promise.resolve()resolves immediately.awaitpauses the function.Schedules a microtask to resume the async function and run B.
Step 4 — console.log('D')
Synchronous continuation outside the async function.
Prints D.
✅ Console: A, D
Step 5 — Microtask: resume → console.log('B')
Async function continues after the first
await.Prints B.
The next line is
await null, which again pauses and schedules another microtask to continue with C.
✅ Console: A, D, B
Step 6 — Microtask: resume → console.log('C')
Async function resumes after
await null.Prints C.
✅ Final Console: A, D, B, C
console.log('start');
Promise.resolve().then(() => console.log('p1'));
queueMicrotask(() => console.log('qm'));
(async () => console.log('iife'))();
console.log('end');
// start
// iife
// end
// p1
// qm
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | queueMicrotask(...) | Microtask queued: qm | start |
| 4 | (async IIFE) call | — | start |
| 5 | Inside IIFE: console.log('iife') | — | start, iife |
| 6 | console.log('end') | Microtasks pending: p1, qm | start, iife, end |
| 7 | (microtask) p1 | Remaining: qm | start, iife, end, p1 |
| 8 | (microtask) qm | — | start, iife, end, p1, qm |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(() => console.log('p1'))
- Schedules microtask p1.
Step 3 — queueMicrotask(() => console.log('qm'))
- Schedules microtask qm.
Step 4 — (async () => console.log('iife'))()
- Async IIFE is called synchronously.
Step 5 — Inside async IIFE: console.log('iife')
Executes synchronously.
Prints iife.
✅ Console: start, iife
Step 6 — console.log('end')
Executes synchronously.
Prints end.
✅ Console: start, iife, end
Step 7 — Microtask: p1
First microtask runs.
Prints p1.
✅ Console: start, iife, end, p1
Step 8 — Microtask: qm
Second microtask runs.
Prints qm.
✅ Final Console: start, iife, end, p1, qm
let p = Promise.resolve();
p.then(() => {
console.log('then1');
p.then(() => console.log('then1.1'));
});
p.then(() => console.log('then2'));
console.log('sync');
// sync
// then1
// then2
// then1.1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('sync') | Microtasks pending: then1, then2 (from p.then) | sync |
| 2 | (microtask) run then1 | New microtask added: then1.1 | sync, then1 |
| 3 | (microtask) run then2 | — | sync, then1, then2 |
| 4 | (microtask) run then1.1 | — | sync, then1, then2, then1.1 |
Step-by-Step Explanation
Step 1 — console.log('sync')
This is the only synchronous operation.
Prints sync.
✅ Console: sync
Step 2 — Microtask: run then1
Runs the first microtask created by
p.then(...).Prints then1.
Inside
then1, we call:
p.then(() => console.log('then1.1'));
- Since
pis already resolved, this immediately queues another microtask:then1.1.
✅ Console: sync, then1
Step 3 — Microtask: run then2
Runs the second microtask from the original code.
Prints then2.
✅ Console: sync, then1, then2
Step 4 — Microtask: run then1.1
Runs last, because it was queued during execution of
then1.Prints then1.1.
✅ Final Console: sync, then1, then2, then1.1
console.log('A');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
queueMicrotask(() => console.log('micro'));
console.log('B');
// A
// B
// promise
// micro
// timeout
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('A') | — | A |
| 2 | setTimeout(...) | Macrotask queued: timeout | A |
| 3 | Promise.resolve().then(...) | Microtask queued: promise | A |
| 4 | queueMicrotask(...) | Microtask queued: micro (after promise) | A |
| 5 | console.log('B') | Microtasks pending: promise, micro | A, B |
| 6 | (microtask) run promise | Remaining: micro | A, B, promise |
| 7 | (microtask) run micro | — | A, B, promise, micro |
| 8 | (macrotask) run timeout | — | A, B, promise, micro, timeout |
Step-by-Step Explanation
Step 1 — console.log('A')
Runs immediately.
Prints A.
✅ Console: A
Step 2 — setTimeout(() => console.log('timeout'), 0)
- Adds timeout to the macrotask queue.
Step 3 — Promise.resolve().then(() => console.log('promise'))
- Adds promise to the microtask queue.
Step 4 — queueMicrotask(() => console.log('micro'))
- Adds micro to the microtask queue, queued after promise.
Step 5 — console.log('B')
Runs synchronously.
Prints B.
✅ Console: A, B
Step 6 — Microtask: run promise
- Prints promise.
✅ Console: A, B, promise
Step 7 — Microtask: run micro
- Prints micro.
✅ Console: A, B, promise, micro
Step 8 — Macrotask: run timeout
- Prints timeout.
✅ Final Console: A, B, promise, micro, timeout
async function outer() {
console.log('o1');
await inner();
console.log('o2');
}
async function inner() {
console.log('i1');
}
outer();
Promise.resolve().then(() => console.log('p'));
console.log('sync');
// o1
// i1
// sync
// o2
// p
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | outer() | — | — |
| 2 | Inside outer: console.log('o1') | — | o1 |
| 3 | Inside outer: await inner() | Microtask: resume outer() → run o2 | o1 |
| 4 | inner() | — | o1 |
| 5 | Inside inner: console.log('i1') | — | o1, i1 |
| 6 | Promise.resolve().then(...) | Microtask queued: p | o1, i1 |
| 7 | console.log('sync') | Microtasks pending: resume outer (o2), p | o1, i1, sync |
| 8 | (microtask) resume async outer() → o2 | Remaining: p | o1, i1, sync, o2 |
| 9 | (microtask) run p | — | o1, i1, sync, o2, p |
Step-by-Step Explanation
Step 1 — outer()
Async function
outer()is called.Execution enters the function.
Step 2 — Inside outer: console.log('o1')
Runs synchronously.
Prints o1.
✅ Console: o1
Step 3 — await inner()
inner()is called.outer()pauses at theawait, scheduling a microtask to resume it later.
Step 4 — inner()
- Async function
inner()starts executing.
Step 5 — Inside inner: console.log('i1')
Runs synchronously.
Prints i1.
✅ Console: o1, i1
Step 6 — Promise.resolve().then(() => console.log('p'))
- Schedules microtask p.
Step 7 — console.log('sync')
Runs synchronously.
Prints sync.
✅ Console: o1, i1, sync
Step 8 — Microtask: resume outer() → console.log('o2')
Async
outer()continues after theawait.Prints o2.
✅ Console: o1, i1, sync, o2
Step 9 — Microtask: p
- Prints p.
✅ Final Console: o1, i1, sync, o2, p
for (let i = 0; i < 3; i++) {
Promise.resolve().then(() => console.log('p', i));
}
console.log('loop end');
// loop end
// p 0
// p 1
// p 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | loop iteration setup | — | — |
| 2 | Promise.resolve().then(...) for i=0 | Microtask queued: p 0 | — |
| 3 | Promise.resolve().then(...) for i=1 | Microtask queued: p 1 | — |
| 4 | Promise.resolve().then(...) for i=2 | Microtask queued: p 2 | — |
| 5 | console.log('loop end') | Microtasks pending: p0, p1, p2 | loop end |
| 6 | (microtask) run p 0 | Remaining: p1, p2 | loop end, p 0 |
| 7 | (microtask) run p 1 | Remaining: p2 | loop end, p 0, p 1 |
| 8 | (microtask) run p 2 | — | loop end, p 0, p 1, p 2 |
Step-by-Step Explanation
Step 1 — Loop starts
- The
forloop runs 3 times (i = 0, 1, 2).
Step 2 — For i = 0: Promise.resolve().then(...)
- Schedules microtask:
console.log('p', 0).
Step 3 — For i = 1: Promise.resolve().then(...)
- Schedules microtask:
console.log('p', 1).
Step 4 — For i = 2: Promise.resolve().then(...)
- Schedules microtask:
console.log('p', 2).
Step 5 — console.log('loop end')
Runs synchronously.
Prints loop end.
✅ Console: loop end
Step 6 — Microtask: run p 0
- Prints p 0.
✅ Console: loop end, p 0
Step 7 — Microtask: run p 1
- Prints p 1.
✅ Console: loop end, p 0, p 1
Step 8 — Microtask: run p 2
- Prints p 2.
✅ Final Console: loop end, p 0, p 1, p 2
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log('t', i), 0);
}
Promise.resolve().then(() => console.log('p'));
console.log('done');
// done
// p
// t 3
// t 3
// t 3
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | loop iteration (0→1→2→3) | Macrotasks queued: t 3, t 3, t 3 | — |
| 2 | Promise.resolve().then(...) | Microtask queued: p | — |
| 3 | console.log('done') | Microtasks pending: p | done |
| 4 | (microtask) p | Macrotasks pending: t 3, t 3, t 3 | done, p |
| 5 | (macrotask) t 3 | Remaining: 2 timeouts | done, p, t 3 |
| 6 | (macrotask) t 3 | Remaining: 1 timeout | done, p, t 3, t 3 |
| 7 | (macrotask) t 3 | — | done, p, t 3, t 3, t 3 |
Step-by-Step Explanation
Step 1 — for loop with var i
var iis function-scoped, so all callbacks share the samei.Loop finishes with
i = 3.All three
setTimeoutcallbacks are queued as macrotasks, each printing t 3 when executed.
Step 2 — Promise.resolve().then(...)
- Schedules a microtask that prints p.
Step 3 — console.log('done')
Runs immediately.
Prints done.
✅ Console: done
Step 4 — Microtask: run p
- Prints p.
✅ Console: done, p
Step 5 — Macrotasks: three setTimeout callbacks
All run one by one:
First callback prints t 3
Second callback prints t 3
Third callback prints t 3
✅ Final Console:done, p, t 3, t 3, t 3
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve().then(() => console.log('p1.1'));
}).then(() => console.log('p2'));
console.log('end');
// start
// end
// p1
// p1.1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | console.log('end') | Microtasks pending: p1 | start, end |
| 4 | (microtask) run p1 | Microtask queued: p1.1 | start, end, p1 |
| 5 | (microtask) run p1.1 | Microtask queued: p2 | start, end, p1, p1.1 |
| 6 | (microtask) run p2 | — | start, end, p1, p1.1, p2 |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(() => { ... })
- Schedules a microtask that will print p1.
Step 3 — console.log('end')
Runs synchronously.
Prints end.
✅ Console: start, end
Step 4 — Microtask: run p1
Prints p1.
The inner
Promise.resolve().then(() => console.log('p1.1'))schedules a new microtask p1.1.
✅ Console: start, end, p1
Step 5 — Microtask: run p1.1
Prints p1.1.
After the entire first chain finishes, its outer
.then(() => console.log('p2'))queues another microtask p2.
✅ Console: start, end, p1, p1.1
Step 6 — Microtask: run p2
- Prints p2.
✅ Final Console: start, end, p1, p1.1, p2
(async () => {
console.log('X');
await Promise.resolve();
console.log('Y');
})();
queueMicrotask(() => console.log('QM'));
Promise.resolve().then(() => console.log('P'));
console.log('S');
// X
// S
// Y
// QM
// P
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (async IIFE) start → console.log('X') | — | X |
| 2 | await Promise.resolve() | Microtask: resume IIFE → Y | X |
| 3 | queueMicrotask(...) | Microtask queued: QM | X |
| 4 | Promise.resolve().then(...) | Microtask queued: P | X |
| 5 | console.log('S') | Microtasks pending: Y, QM, P | X, S |
| 6 | (microtask) resume async → Y | Remaining: QM, P | X, S, Y |
| 7 | (microtask) QM | Remaining: P | X, S, Y, QM |
| 8 | (microtask) P | — | X, S, Y, QM, P |
Step-by-Step Explanation
Step 1 — async IIFE begins → console.log('X')
The IIFE starts executing immediately.
Prints X.
✅ Console: X
Step 2 — await Promise.resolve()
Pauses the async function.
Schedules a microtask that will later print Y.
Step 3 — queueMicrotask(() => console.log('QM'))
- Schedules microtask QM.
Step 4 — Promise.resolve().then(() => console.log('P'))
- Schedules microtask P.
Step 5 — console.log('S')
- Final synchronous line prints S.
✅ Console: X, S
Step 6 — Microtask: resume async → console.log('Y')
Async function continues.
Prints Y.
✅ Console: X, S, Y
Step 7 — Microtask: QM
- Prints QM.
✅ Console: X, S, Y, QM
Step 8 — Microtask: P
- Prints P.
✅ Final Console: X, S, Y, QM, P
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => console.log('q inside p'));
});
queueMicrotask(() => console.log('q0'));
console.log('e');
// s
// e
// p1
// q0
// q inside p
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | s |
| 3 | queueMicrotask(() => console.log('q0')) | Microtasks: p1, q0 | s |
| 4 | console.log('e') | Microtasks pending | s, e |
| 5 | (microtask) run p1 | Inside: queueMicrotask → adds q inside p | s, e, p1 |
| 6 | (microtask) run q0 | Remaining: q inside p | s, e, p1, q0 |
| 7 | (microtask) run q inside p | — | s, e, p1, q0, q inside p |
Step-by-Step Explanation
Step 1 — console.log('s')
Runs immediately.
Prints s.
✅ Console: s
Step 2 — Promise.resolve().then(...)
.then()schedules microtask p1.
Step 3 — queueMicrotask(() => console.log('q0'))
Schedules microtask q0.
Current microtask order:
p1 → q0.
Step 4 — console.log('e')
- Prints e.
✅ Console: s, e
Step 5 — Microtask: run p1
Prints p1.
Inside
p1, another microtask is queued: q inside p.
Microtask queue now: q0 → q inside p.
✅ Console: s, e, p1
Step 6 — Microtask: run q0
- Prints q0.
Next microtask: q inside p.
✅ Console: s, e, p1, q0
Step 7 — Microtask: run q inside p
- Prints q inside p.
This is the final microtask.
✅ Final Console: s, e, p1, q0, q inside p
async function a() {
console.log('a start');
await Promise.resolve().then(() => console.log('a inner'));
console.log('a end');
}
a();
console.log('after');
// a start
// after
// a inner
// a end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | a() | — | — |
| 2 | Inside a: console.log('a start') | — | a start |
| 3 | Inside a: await Promise.resolve().then(...) | Microtask: run a inner → then resume a() with a end | a start |
| 4 | console.log('after') | Microtasks pending: a inner, then resume async → a end | a start, after |
| 5 | (microtask) run a inner | Next microtask queued: resume async part → a end | a start, after, a inner |
| 6 | (microtask) resume async → console.log('a end') | — | a start, after, a inner, a end |
Step-by-Step Explanation
Step 1 — a()
Async function
a()is called.Execution enters the function.
Step 2 — Inside a: console.log('a start')
Runs immediately.
Prints a start.
✅ Console: a start
Step 3 — await Promise.resolve().then(() => console.log('a inner'))
The
.then()schedules microtask a inner.awaitpausesa()and schedules another microtask to later continuea()and print a end.
Step 4 — console.log('after')
Executes synchronously after calling
a().Prints after.
✅ Console: a start, after
Step 5 — Microtask: run a inner
- Prints a inner.
✅ Console: a start, after, a inner
Step 6 — Microtask: resume async → console.log('a end')
Async function continues after the awaited chain.
Prints a end.
✅ Final Console: a start, after, a inner, a end
setTimeout(() => console.log('t1'), 0);
Promise.resolve().then(() => {
console.log('p1');
setTimeout(() => console.log('t2'), 0);
});
console.log('sync');
// sync
// p1
// t1
// t2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | setTimeout(t1) | Macrotask queued: t1 | — |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | — |
| 3 | console.log('sync') | Microtask pending: p1 | sync |
| 4 | (microtask) run p1 | Macrotask added: t2 | sync, p1 |
| 5 | (macrotask) run t1 | Remaining: t2 | sync, p1, t1 |
| 6 | (macrotask) run t2 | — | sync, p1, t1, t2 |
Step-by-Step Explanation
Step 1 — setTimeout(() => console.log('t1'), 0)
- Schedules macrotask t1.
Step 2 — Promise.resolve().then(...)
- Schedules microtask p1.
Step 3 — console.log('sync')
Executes synchronously.
Prints sync.
✅ Console: sync
Step 4 — Microtask: run p1
Prints p1.
Inside it, another
setTimeout(() => console.log('t2'))schedules macrotask t2.
✅ Console: sync, p1
Step 5 — Macrotask: run t1
- Prints t1.
✅ Console: sync, p1, t1
Step 6 — Macrotask: run t2
- Prints t2.
✅ Final Console: sync, p1, t1, t2
console.log('1');
Promise.resolve().then(() => console.log('2'));
(async () => {
await 0;
console.log('3');
})();
console.log('4');
// 1
// 4
// 2
// 3
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('1') | — | 1 |
| 2 | Promise.resolve().then(...) | Microtask queued: 2 | 1 |
| 3 | async IIFE call | — | 1 |
| 4 | inside async: await 0 | Microtask queued: resume async → 3 | 1 |
| 5 | console.log('4') | Microtasks pending: 2, 3 | 1, 4 |
| 6 | (microtask) run 2 | Remaining: 3 | 1, 4, 2 |
| 7 | (microtask) run 3 | — | 1, 4, 2, 3 |
Step-by-Step Explanation
Step 1 — console.log('1')
Runs immediately.
Prints 1.
✅ Console: 1
Step 2 — Promise.resolve().then(() => console.log('2'))
- Schedules microtask 2.
Step 3 — Async IIFE executes
- Enters the async function.
Step 4 — await 0
Pauses the async function.
Schedules a microtask to later print 3.
Step 5 — console.log('4')
Runs synchronously.
Prints 4.
✅ Console: 1, 4
Step 6 — Microtask: 2
- Prints 2.
✅ Console: 1, 4, 2
Step 7 — Microtask: async resumes → 3
Async function continues.
Prints 3.
✅ Final Console: 1, 4, 2, 3
let x = Promise.resolve(1);
x.then(v => {
console.log('a', v);
return 2;
}).then(v => console.log('b', v));
Promise.resolve().then(() => console.log('c'));
// a 1
// c
// b 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | Promise.resolve(1) → stored in x | — | — |
| 2 | x.then(...) | Microtask queued: a 1 | — |
| 3 | second .then(...) registered | (runs after a 1) | — |
| 4 | Promise.resolve().then(...) | Microtask queued: c | — |
| 5 | (end of sync) | Microtasks pending: a1, b2, c | — |
| 6 | (microtask) run first then → a 1 | Microtask queued: b 2 | a 1 |
| 7 | (microtask) run second then → b 2 | Remaining: c | a 1, b 2 |
| 8 | (microtask) run c | — | a 1, b 2, c |
Step-by-Step Explanation
Step 1 — let x = Promise.resolve(1)
Creates an already-resolved promise with value 1.
No console output.
Step 2 — x.then(v => {...})
- Schedules a microtask that will later print a 1.
Step 3 — .then(v => console.log('b', v))
This second
.thenis chained.It will run only after the first
.thenresolves and returns2.
Step 4 — Promise.resolve().then(() => console.log('c'))
- Adds another microtask: c.
Step 5 — End of synchronous code
Nothing printed yet.
Pending microtasks (in order):
a 1b 2c
Step 6 — Microtask: first .then → prints a 1
Runs with value 1.
Prints a 1.
Returns 2, which queues next microtask: b 2.
✅ Console: a 1
Step 7 — Microtask: second .then → prints b 2
- Runs with value 2.
✅ Console: a 1, b 2
Step 8 — Microtask: c
- Prints c.
✅ Final Console: a 1, b 2, c
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve().then(() => console.log('p1.1'));
}).then(() => console.log('p2'));
console.log('e');
// s
// e
// p1
// p1.1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | s |
| 3 | console.log('e') | Microtasks pending: p1 | s, e |
| 4 | (microtask) run p1 | Microtask queued: p1.1 | s, e, p1 |
| 5 | (microtask) run p1.1 | Task chained → microtask queued: p2 | s, e, p1, p1.1 |
| 6 | (microtask) run p2 | — | s, e, p1, p1.1, p2 |
Step-by-Step Explanation
Step 1 — console.log('s')
Executes immediately.
Prints s.
✅ Console: s
Step 2 — Promise.resolve().then(...)
- Registers a microtask that will run
p1.
Step 3 — console.log('e')
Executes synchronously.
Prints e.
✅ Console: s, e
Step 4 — Microtask: run p1
Prints p1.
The inner
return Promise.resolve().then(...)queues microtask p1.1.
✅ Console: s, e, p1
Step 5 — Microtask: run p1.1
Prints p1.1.
After this finishes, the chained
.then(() => console.log('p2'))schedules microtask p2.
✅ Console: s, e, p1, p1.1
Step 6 — Microtask: run p2
- Prints p2.
✅ Final Console: s, e, p1, p1.1, p2
async function f() {
console.log('f1');
await Promise.resolve();
console.log('f2');
}
Promise.resolve().then(() => console.log('p'));
f();
console.log('sync');
// f1
// sync
// p
// f2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | Promise.resolve().then(...) | Microtask queued: p | — |
| 2 | f() | — | — |
| 3 | Inside f: console.log('f1') | — | f1 |
| 4 | Inside f: await Promise.resolve() | Microtask queued: resume f() → f2 | f1 |
| 5 | console.log('sync') | Microtasks pending: p, f2 | f1, sync |
| 6 | (microtask) p | Remaining: f2 | f1, sync, p |
| 7 | (microtask) resume async → f2 | — | f1, sync, p, f2 |
Step-by-Step Explanation
Step 1 — Promise.resolve().then(() => console.log('p'))
- Schedules a microtask that will print p later.
Step 2 — f()
Async function
f()is called.Execution enters the function.
Step 3 — Inside f: console.log('f1')
Synchronous.
Prints f1.
✅ Console: f1
Step 4 — Inside f: await Promise.resolve()
awaitpausesf().Schedules a microtask that will later print f2.
Step 5 — console.log('sync')
Executes synchronously.
Prints sync.
✅ Console: f1, sync
Step 6 — Microtask: run p
- Prints p.
✅ Console: f1, sync, p
Step 7 — Microtask: resume async → console.log('f2')
f()continues after theawait.Prints f2.
✅ Final Console: f1, sync, p, f2
console.log('start');
Promise.resolve().then(() => console.log('p1'));
queueMicrotask(() => console.log('qm1'));
Promise.resolve().then(() => console.log('p2'));
console.log('end');
// start
// end
// p1
// qm1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(p1) | Microtask queued: p1 | start |
| 3 | queueMicrotask(qm1) | Microtask queued: qm1 | start |
| 4 | Promise.resolve().then(p2) | Microtask queued: p2 | start |
| 5 | console.log('end') | Microtasks pending: p1, qm1, p2 | start, end |
| 6 | (microtask) run p1 | Remaining: qm1, p2 | start, end, p1 |
| 7 | (microtask) run qm1 | Remaining: p2 | start, end, p1, qm1 |
| 8 | (microtask) run p2 | — | start, end, p1, qm1, p2 |
Step-by-Step Explanation
Step 1 — console.log('start')
Executes immediately.
Prints start.
✅ Console: start
Step 2 — .then(() => console.log('p1'))
- Schedules microtask p1.
Step 3 — queueMicrotask(() => console.log('qm1'))
- Schedules microtask qm1.
Step 4 — .then(() => console.log('p2'))
- Schedules microtask p2.
Step 5 — console.log('end')
Executes synchronously.
Prints end.
✅ Console: start, end
Step 6 — Microtask: p1
Runs first in the microtask queue.
Prints p1.
✅ Console: start, end, p1
Step 7 — Microtask: qm1
Executes next.
Prints qm1.
✅ Console: start, end, p1, qm1
Step 8 — Microtask: p2
Final microtask.
Prints p2.
✅ Final Console: start, end, p1, qm1, p2
(async () => {
console.log('a');
await new Promise(res => {
console.log('executor');
res();
});
console.log('b');
})();
console.log('c');
// a
// executor
// c
// b
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (async IIFE) call | — | — |
| 2 | Inside IIFE: console.log('a') | — | a |
| 3 | new Promise(res => { console.log('executor'); res(); }) | Microtask queued: resume → print b | a, executor |
| 4 | console.log('c') | Microtasks pending: resume async | a, executor, c |
| 5 | (microtask) resume async → console.log('b') | — | a, executor, c, b |
Step-by-Step Explanation
Step 1 — Async IIFE runs
The async IIFE is invoked immediately.
Execution enters the function body.
Step 2 — console.log('a')
Runs synchronously.
Prints a.
✅ Console: a
Step 3 — await new Promise(...)
The Promise executor runs immediately.
Inside the executor:
console.log('executor')prints executorres()resolves the Promise instantly
Because of
await, the async function pauses.The continuation (printing b) is placed in the Microtask Queue.
✅ Console: a, executor
Step 4 — console.log('c')
Runs after the async function yields.
Prints c.
✅ Console: a, executor, c
Step 5 — Microtask: resume async → console.log('b')
Async function resumes after the awaited Promise.
Prints b.
✅ Final Console: a, executor, c, b
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => console.log('q1'));
});
queueMicrotask(() => console.log('q0'));
console.log('e');
// s
// e
// p1
// q0
// q1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | s |
| 3 | queueMicrotask(q0) | Microtask queued: q0 | s |
| 4 | console.log('e') | Microtasks pending: p1, q0 | s, e |
| 5 | (microtask) run p1 | queues q1 | s, e, p1 |
| 6 | (microtask) run q0 | Microtasks: q1 | s, e, p1, q0 |
| 7 | (microtask) run q1 | — | s, e, p1, q0, q1 |
Step-by-Step Explanation
Step 1 — console.log('s')
Executes immediately.
Prints s.
✅ Console: s
Step 2 — Promise.resolve().then(...)
- Schedules microtask p1.
Step 3 — queueMicrotask(() => console.log('q0'))
- Schedules microtask q0.
Step 4 — console.log('e')
Executes synchronously.
Prints e.
✅ Console: s, e
Step 5 — Microtask: run p1
Prints p1.
Inside it,
queueMicrotask(q1)schedules q1.
✅ Console: s, e, p1
Step 6 — Microtask: run q0
Runs next (queued earlier than q1).
Prints q0.
✅ Console: s, e, p1, q0
Step 7 — Microtask: run q1
Runs last.
Prints q1.
✅ Final Console: s, e, p1, q0, q1
(async function main() {
console.log('m1');
await Promise.resolve();
console.log('m2');
await Promise.resolve();
console.log('m3');
})();
console.log('after main');
// m1
// after main
// m2
// m3
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | main() | — | — |
| 2 | Inside main: console.log('m1') | — | m1 |
| 3 | Inside main: await Promise.resolve() | Microtask queued: resume → run m2 | m1 |
| 4 | console.log('after main') | Microtask pending: m2 | m1, after main |
| 5 | (microtask) resume main → console.log('m2') | Microtask queued: resume → run m3 | m1, after main, m2 |
| 6 | (microtask) resume main after second await → console.log('m3') | — | m1, after main, m2, m3 |
Step-by-Step Explanation
Step 1 — main()
The async IIFE
main()begins running immediately.Execution moves inside the function.
Step 2 — console.log('m1')
Runs synchronously.
Prints m1.
✅ Console: m1
Step 3 — First await Promise.resolve()
awaitpausesmain().A microtask is scheduled to continue with
console.log('m2').
Step 4 — console.log('after main')
Runs while
main()is paused.Prints after main.
✅ Console: m1, after main
Step 5 — Microtask: resume → console.log('m2')
Async function continues after first await.
Prints m2.
The second
await Promise.resolve()pauses again and schedules another microtask.
✅ Console: m1, after main, m2
Step 6 — Microtask: resume → console.log('m3')
Async function resumes after second await.
Prints m3.
✅ Final Console: m1, after main, m2, m3
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve('v');
}).then(v => console.log('then', v));
console.log('end');
// start
// end
// p1
// then v
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | .then(v => console.log('then', v)) is registered | Will queue after p1 | start |
| 4 | console.log('end') | Microtasks: p1 then then v | start, end |
| 5 | (microtask) run p1 | Schedules next microtask: then('v') | start, end, p1 |
| 6 | (microtask) run then('v') | — | start, end, p1, then v |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(() => {...})
.then()schedules a microtask.Microtask p1 is queued.
Step 3 — Second .then(v => console.log('then', v))
Registered in the promise chain.
Will run after p1 resolves and returns
"v".
Step 4 — console.log('end')
Last synchronous line.
Prints end.
✅ Console: start, end
Step 5 — Microtask: run p1
Prints p1.
Returns
Promise.resolve('v'), which schedules microtask then v.
✅ Console: start, end, p1
Step 6 — Microtask: run then v
- Prints then v.
✅ Final Console: start, end, p1, then v
for (let i = 0; i < 2; i++) {
(async () => {
console.log('ai', i);
await null;
console.log('ai after', i);
})();
}
console.log('loop done');
// ai 0
// ai 1
// loop done
// ai after 0
// ai after 1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | for loop (i=0) → async IIFE call | — | — |
| 2 | IIFE(0): console.log('ai', 0) | — | ai 0 |
| 3 | IIFE(0): await null | Microtask queued: resume IIFE(0) → ai after 0 | ai 0 |
| 4 | for loop (i=1) → async IIFE call | — | ai 0 |
| 5 | IIFE(1): console.log('ai', 1) | — | ai 0, ai 1 |
| 6 | IIFE(1): await null | Microtask queued: resume IIFE(1) → ai after 1 | ai 0, ai 1 |
| 7 | console.log('loop done') | Microtasks pending: ai after 0, ai after 1 | ai 0, ai 1, loop done |
| 8 | (microtask) resume IIFE(0) → console.log('ai after', 0) | Remaining: IIFE(1) | ai 0, ai 1, loop done, ai after 0 |
| 9 | (microtask) resume IIFE(1) → console.log('ai after', 1) | — | ai 0, ai 1, loop done, ai after 0, ai after 1 |
Step-by-Step Explanation
Step 1 — Loop iteration i = 0
Async IIFE is invoked immediately.
Control enters IIFE.
Step 2 — Inside IIFE(0): console.log('ai', 0)
Runs synchronously.
Prints ai 0.
✅ Console: ai 0
Step 3 — await null (IIFE 0)
await nullyields immediately.Schedules microtask to resume IIFE(0) which will print ai after 0 later.
Step 4 — Loop iteration i = 1
Async IIFE is invoked again (new instance).
Control enters IIFE.
Step 5 — Inside IIFE(1): console.log('ai', 1)
Runs synchronously.
Prints ai 1.
✅ Console: ai 0, ai 1
Step 6 — await null (IIFE 1)
- Schedules microtask to later print ai after 1.
Step 7 — console.log('loop done')
Last synchronous line executes.
Prints loop done.
✅ Console: ai 0, ai 1, loop done
Step 8 — Microtask: resume IIFE(0) → console.log('ai after', 0)
First microtask runs.
Prints ai after 0.
✅ Console: ai 0, ai 1, loop done, ai after 0
Step 9 — Microtask: resume IIFE(1) → console.log('ai after', 1)
Second microtask runs.
Prints ai after 1.
✅ Final Console: ai 0, ai 1, loop done, ai after 0, ai after 1
let p = Promise.resolve();
p.then(() => console.log('1')).then(() => console.log('2'));
queueMicrotask(() => console.log('qm'));
console.log('sync');
// sync
// 1
// qm
// 2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('sync') | — | sync |
| 2 | p.then(() => console.log('1')) | Microtask queued: 1 | sync |
| 3 | queueMicrotask(() => console.log('qm')) | Microtask queued: qm | sync |
| 4 | (microtask) run 1 | Microtask queued: 2 | sync, 1 |
| 5 | (microtask) run qm | Remaining: 2 | sync, 1, qm |
| 6 | (microtask) run 2 | — | sync, 1, qm, 2 |
Step-by-Step Explanation
Step 1 — console.log('sync')
Runs synchronously.
Prints sync.
✅ Console: sync
Step 2 — p.then(() => console.log('1'))
- Schedules a microtask that will later print 1.
Step 3 — queueMicrotask(() => console.log('qm'))
Schedules microtask qm.
Microtasks now (in order):
1qm
Step 4 — Microtask: run 1
Prints 1.
Its chained
.then(() => console.log('2'))schedules microtask 2.
Microtasks now:
qm2
✅ Console: sync, 1
Step 5 — Microtask: run qm
- Prints qm.
Microtasks now:
2
✅ Console: sync, 1, qm
Step 6 — Microtask: run 2
- Prints 2.
✅ Final Console: sync, 1, qm, 2
async function a() {
console.log('A');
await Promise.resolve();
console.log('B');
}
async function b() {
console.log('C');
await Promise.resolve();
console.log('D');
}
a(); b();
console.log('E');
// A
// C
// E
// B
// D
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | a() | — | — |
| 2 | Inside a: console.log('A') | — | A |
| 3 | Inside a: await Promise.resolve() | Microtask: resume a() → B | A |
| 4 | b() | — | A |
| 5 | Inside b: console.log('C') | — | A, C |
| 6 | Inside b: await Promise.resolve() | Microtask: resume b() → D | A, C |
| 7 | console.log('E') | Microtasks pending: B, D | A, C, E |
| 8 | (microtask) resume a() → B | Remaining: D | A, C, E, B |
| 9 | (microtask) resume b() → D | — | A, C, E, B, D |
Step-by-Step Explanation
Step 1 — a()
Async function
ais called.Execution enters the function.
Step 2 — Inside a: console.log('A')
Runs immediately.
Prints A.
✅ Console: A
Step 3 — await Promise.resolve() inside a
Pauses
a.Schedules a microtask to later continue with
B.
Step 4 — b()
Async function
bis called.Execution enters the function.
Step 5 — Inside b: console.log('C')
Runs immediately.
Prints C.
✅ Console: A, C
Step 6 — await Promise.resolve() inside b
Pauses
b.Schedules a microtask to later continue with
D.
Step 7 — console.log('E')
Synchronous.
Prints E.
✅ Console: A, C, E
Step 8 — Microtask: resume a() → print B
Async
acontinues.Prints B.
✅ Console: A, C, E, B
Step 9 — Microtask: resume b() → print D
Async
bcontinues.Prints D.
✅ Final Console: A, C, E, B, D
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => {
console.log('q1');
Promise.resolve().then(() => console.log('p-in-q'));
});
});
console.log('end');
// start
// end
// p1
// q1
// p-in-q
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | console.log('end') | Microtasks pending: p1 | start, end |
| 4 | (microtask) run p1 | queueMicrotask: q1 | start, end, p1 |
| 5 | (microtask) run q1 | Microtask queued: p-in-q | start, end, p1, q1 |
| 6 | (microtask) run p-in-q | — | start, end, p1, q1, p-in-q |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(...)
.then()schedules a microtask that will later print p1.
Step 3 — console.log('end')
Runs synchronously.
Prints end.
✅ Console: start, end
Step 4 — Microtask: run p1
Prints p1.
Inside
p1,queueMicrotaskschedules q1.
✅ Console: start, end, p1
Step 5 — Microtask: run q1
Prints q1.
Inside
q1, a newPromise.resolve().then(...)schedules p-in-q.
✅ Console: start, end, p1, q1
Step 6 — Microtask: run p-in-q
- Prints p-in-q.
✅ Final Console: start, end, p1, q1, p-in-q
(async () => {
console.log('s');
await Promise.resolve();
console.log('p');
await null;
console.log('q');
})();
Promise.resolve().then(() => console.log('then1'));
console.log('after');
// s
// after
// p
// then1
// q
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | IIFE call | — | — |
| 2 | console.log('s') | — | s |
| 3 | await Promise.resolve() | Microtask queued: resume → p | s |
| 4 | Promise.resolve().then(...) | Microtask queued: then1 | s |
| 5 | console.log('after') | Microtasks pending: p, then1 | s, after |
| 6 | (microtask) resume IIFE → console.log('p') | Microtask queued: resume for q | s, after, p |
| 7 | (microtask) then1 | Remaining microtask: resume → q | s, after, p, then1 |
| 8 | (microtask) resume IIFE → console.log('q') | — | s, after, p, then1, q |
Step-by-Step Explanation
Step 1 — IIFE begins
Async IIFE is invoked.
Execution enters the function.
Step 2 — console.log('s')
Runs immediately.
Prints s.
✅ Console: s
Step 3 — await Promise.resolve()
Promise.resolve()resolves instantly.awaitpauses the async function.Queues a microtask to resume later (to run
console.log('p')).
Step 4 — Promise.resolve().then(() => console.log('then1'))
- Schedules microtask then1.
Step 5 — console.log('after')
Runs synchronously.
Prints after.
✅ Console: s, after
Step 6 — Microtask: resume async → console.log('p')
Async function continues after the first
await.Prints p.
Next
await nullschedules another microtask for later (q).
✅ Console: s, after, p
Step 7 — Microtask: then1
- Prints then1.
✅ Console: s, after, p, then1
Step 8 — Microtask: resume async → console.log('q')
Async function resumes after
await null.Prints q.
✅ Final Console: s, after, p, then1, q
console.log('start');
Promise.resolve().then(() => console.log('p1'));
setTimeout(() => console.log('t1'), 0);
new Promise(res => res()).then(() => console.log('p2'));
console.log('end');
// start
// end
// p1
// p2
// t1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | setTimeout(..., 0) | Macrotask queued: t1 | start |
| 4 | new Promise(res=>res()).then(...) | Microtask queued: p2 | start |
| 5 | console.log('end') | Microtasks pending: p1, p2 | start, end |
| 6 | (microtask) run p1 | Remaining microtasks: p2 | start, end, p1 |
| 7 | (microtask) run p2 | — | start, end, p1, p2 |
| 8 | (macrotask) run t1 | — | start, end, p1, p2, t1 |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(() => console.log('p1'))
- Schedules microtask p1.
Step 3 — setTimeout(() => console.log('t1'), 0)
- Schedules macrotask t1.
Step 4 — new Promise(res=>res()).then(() => console.log('p2'))
The executor resolves instantly.
Schedules microtask p2.
Step 5 — console.log('end')
- Prints end.
✅ Console: start, end
Step 6 — Microtask: p1
- Prints p1.
✅ Console: start, end, p1
Step 7 — Microtask: p2
- Prints p2.
✅ Console: start, end, p1, p2
Step 8 — Macrotask: t1
Event loop moves to macrotasks.
Prints t1.
✅ Final Console: start, end, p1, p2, t1
(async () => {
await Promise.resolve();
console.log('a');
})();
Promise.resolve().then(() => console.log('b'));
queueMicrotask(() => console.log('c'));
console.log('d');
// d
// a
// b
// c
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (async IIFE call) | — | — |
| 2 | Inside IIFE: await Promise.resolve() | Microtask queued: resume → a | — |
| 3 | Promise.resolve().then(...) | Microtask queued: b | — |
| 4 | queueMicrotask(...) | Microtask queued: c | — |
| 5 | console.log('d') | Microtasks pending: a, b, c | d |
| 6 | (microtask) resume async → a | Remaining: b, c | d, a |
| 7 | (microtask) b | Remaining: c | d, a, b |
| 8 | (microtask) c | — | d, a, b, c |
Step-by-Step Explanation
Step 1 — Async IIFE runs
The async function starts execution.
Hits the first line:
await Promise.resolve().
Step 2 — await Promise.resolve()
The promise resolves immediately.
The continuation (logging a) is placed into the Microtask Queue.
Execution of the async function pauses.
Step 3 — Promise.resolve().then(() => console.log('b'))
- Schedules a microtask that will print b.
Step 4 — queueMicrotask(() => console.log('c'))
- Schedules microtask c.
Step 5 — console.log('d')
Runs synchronously.
Prints d.
✅ Console: d
Step 6 — Microtask: resume async → console.log('a')
Execution continues after the
await.Prints a.
✅ Console: d, a
Step 7 — Microtask: b
Runs next.
Prints b.
✅ Console: d, a, b
Step 8 — Microtask: c
- Final microtask prints c.
✅ Final Console: d, a, b, c
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
return Promise.resolve().then(() => console.log('p1.1'));
}).then(() => console.log('p2'));
queueMicrotask(() => console.log('qm'));
console.log('e');
// s
// e
// p1
// qm
// p1.1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask: p1 | s |
| 3 | queueMicrotask(...) | Microtask: qm (after p1) | s |
| 4 | console.log('e') | Microtasks pending: p1, qm | s, e |
| 5 | (microtask) run p1 | New microtask added: p1.1 | s, e, p1 |
| 6 | (microtask) run p1.1 | Next: p2, qm | s, e, p1, p1.1 |
| 7 | (microtask) run p2 | Next: qm | s, e, p1, p1.1, p2 |
| 8 | (microtask) run qm | — | s, e, p1, p1.1, p2, qm |
Step-by-Step Explanation
Step 1 — console.log('s')
Runs immediately.
Prints s.
✅ Console: s
Step 2 — First .then() schedules microtask p1
Promise.resolve().then(...)registers a microtask.p1is added to the Microtask Queue.
Step 3 — queueMicrotask(() => console.log('qm'))
- Schedules another microtask qm, placed after
p1.
Step 4 — console.log('e')
- Prints e.
✅ Console: s, e
Step 5 — Microtask: p1
Runs
p1.Prints p1.
Inside it, a new promise resolves and schedules microtask p1.1.
✅ Console: s, e, p1
Step 6 — Microtask: p1.1
Runs next.
Prints p1.1.
✅ Console: s, e, p1, p1.1
Step 7 — Microtask: p2
The parent
.then()resolves afterp1.Prints p2.
✅ Console: s, e, p1, p1.1, p2
Step 8 — Microtask: qm
Finally the queued microtask qm runs.
Prints qm.
✅ Final Console: s, e, p1, p1.1, p2, qm
(async function test() {
console.log('x');
await 0;
console.log('y');
})();
setTimeout(() => console.log('t'), 0);
Promise.resolve().then(() => console.log('p'));
console.log('z');
// x
// z
// y
// p
// t
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('x') | — | x |
| 2 | await 0 suspends async | Microtask: resume → y | x |
| 3 | setTimeout(...,0) | Macrotask queued: t | x |
| 4 | Promise.resolve().then(...) | Microtask queued: p | x |
| 5 | console.log('z') | Microtasks pending: y, p | x, z |
| 6 | (microtask) resume async → y | Remaining: p | x, z, y |
| 7 | (microtask) run p | — | x, z, y, p |
| 8 | (macrotask) run t | — | x, z, y, p, t |
Step-by-Step Explanation
Step 1 — console.log('x')
Runs immediately inside the async IIFE.
Prints x.
✅ Console: x
Step 2 — await 0
await 0is equivalent toawait Promise.resolve(0).The async function pauses.
A microtask is queued to resume later and run
console.log('y').
Step 3 — setTimeout(() => console.log('t'), 0)
- Schedules a macrotask:
t.
Step 4 — Promise.resolve().then(() => console.log('p'))
- Schedules microtask p.
Step 5 — console.log('z')
Runs synchronously.
Prints z.
✅ Console: x, z
Step 6 — Microtask: resume async → console.log('y')
Async function continues after
await.Prints y.
✅ Console: x, z, y
Step 7 — Microtask: p
- Prints p.
✅ Console: x, z, y, p
Step 8 — Macrotask: t
Event loop moves to macrotasks.
Prints t.
✅ Final Console: x, z, y, p, t
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
queueMicrotask(() => console.log('q1'));
}).then(() => console.log('p2'));
queueMicrotask(() => console.log('q0'));
console.log('end');
// start
// end
// p1
// q0
// q1
// p2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | queueMicrotask(q0) | Microtask queued: q0 | start |
| 4 | console.log('end') | Microtasks pending: p1, q0 | start, end |
| 5 | (microtask) run p1 | queues: q1, and schedules next .then → microtask p2 | start, end, p1 |
| 6 | (microtask) run p2 | pending: q0, q1 | start, end, p1, p2 |
| 7 | (microtask) run q0 | pending: q1 | start, end, p1, p2, q0 |
| 8 | (microtask) run q1 | — | start, end, p1, p2, q0, q1 |
Step-by-Step Explanation
Step 1 — console.log('start')
Executes immediately.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(...)
- Schedules the microtask p1.
Step 3 — queueMicrotask(() => console.log('q0'))
- Schedules q0.
Step 4 — console.log('end')
Runs synchronously.
Prints end.
✅ Console: start, end
Step 5 — Microtask: run p1
Prints p1.
Inside
p1:Schedules q1.
The chained
.then(() => console.log('p2'))schedules microtask p2.
✅ Console: start, end, p1
Step 6 — Microtask: p2
- Prints p2.
✅ Console: start, end, p1, p2
Step 7 — Microtask: q0
- Prints q0.
✅ Console: start, end, p1, p2, q0
Step 8 — Microtask: q1
- Prints q1.
✅ Final Console: start, end, p1, p2, q0, q1
(async () => {
console.log('A');
await Promise.resolve();
console.log('B');
Promise.resolve().then(() => console.log('C'));
})();
console.log('D');
// A
// D
// B
// C
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | IIFE call | — | — |
| 2 | console.log('A') | — | A |
| 3 | await Promise.resolve() | Microtask: resume → print B | A |
| 4 | console.log('D') | Microtasks pending: B | A, D |
| 5 | (microtask) resume async → console.log('B') | Microtask queued: C | A, D, B |
| 6 | (microtask) run C | — | A, D, B, C |
Step-by-Step Explanation
Step 1 — Start async IIFE
- The async function executes immediately.
Step 2 — console.log('A')
Runs synchronously.
Prints A.
✅ Console: A
Step 3 — await Promise.resolve()
awaitpauses the function.Schedules a microtask that will later print B.
Step 4 — console.log('D')
Next synchronous statement.
Prints D.
✅ Console: A, D
Step 5 — Microtask: resume async → console.log('B')
Async function continues after the
await.Prints B.
Inside it,
Promise.resolve().then(() => console.log('C'))schedules microtask C.
✅ Console: A, D, B
Step 6 — Microtask: run C
- Prints C.
✅ Final Console: A, D, B, C
console.log('s');
Promise.resolve().then(() => {
console.log('p1');
Promise.resolve().then(() => console.log('p1.1'));
});
queueMicrotask(() => console.log('qm'));
console.log('e');
// s
// e
// p1
// qm
// p1.1
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('s') | — | s |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | s |
| 3 | queueMicrotask(...) | Microtask queued: qm | s |
| 4 | console.log('e') | Microtasks pending: p1, qm | s, e |
| 5 | (microtask) run p1 | New microtask queued: p1.1 | s, e, p1 |
| 6 | (microtask) run p1.1 | Remaining: qm | s, e, p1, p1.1 |
| 7 | (microtask) run qm | — | s, e, p1, p1.1, qm |
Step-by-Step Explanation
Step 1 — console.log('s')
Runs immediately.
Prints s.
✅ Console: s
Step 2 — Promise.resolve().then(...)
.then()schedules microtask p1.
Step 3 — queueMicrotask(() => console.log('qm'))
- Microtask qm is added after p1.
Step 4 — console.log('e')
- Synchronous log prints e.
✅ Console: s, e
Step 5 — Microtask: run p1
Prints p1.
Inside
p1, anotherPromise.resolve().thenschedules p1.1.
✅ Console: s, e, p1
Step 6 — Microtask: run p1.1
- Prints p1.1.
✅ Console: s, e, p1, p1.1
Step 7 — Microtask: run qm
- Prints qm.
✅ Final Console: s, e, p1, p1.1, qm
(async function run() {
console.log('start');
await Promise.resolve();
console.log('mid');
await Promise.resolve();
console.log('end');
})();
console.log('after');
// start
// after
// mid
// end
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | (async run()) | — | — |
| 2 | Inside run: console.log('start') | — | start |
| 3 | Inside run: await Promise.resolve() | Microtask queued: resume → print mid | start |
| 4 | console.log('after') | Microtasks pending: mid | start, after |
| 5 | (microtask) resume async → console.log('mid') | Microtask queued: resume → print end | start, after, mid |
| 6 | (microtask) resume async → console.log('end') | — | start, after, mid, end |
Step-by-Step Explanation
Step 1 — Call run() (async IIFE)
- The async function starts executing immediately.
Step 2 — Inside run: console.log('start')
- Prints start.
✅ Console: start
Step 3 — await Promise.resolve()
Pauses
run()execution.Schedules a microtask to resume later and print mid.
Step 4 — console.log('after')
Synchronous code outside the async runs.
Prints after.
✅ Console: start, after
Step 5 — Microtask: resume async → console.log('mid')
First microtask resumes the async function.
Prints mid.
The next
await Promise.resolve()schedules another microtask to print end.
✅ Console: start, after, mid
Step 6 — Microtask: resume async → console.log('end')
- Final microtask prints end.
✅ Final Console: start, after, mid, end
console.log('1');
Promise.resolve().then(() => {
console.log('2');
queueMicrotask(() => console.log('2.qm'));
});
queueMicrotask(() => console.log('qm1'));
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 1
// 4
// 2
// qm1
// 3
// 2.qm
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('1') | — | 1 |
| 2 | Promise.resolve().then(...) | Microtask: 2 | 1 |
| 3 | queueMicrotask(...) | Microtask: 2, qm1 | 1 |
| 4 | Promise.resolve().then(...) | Microtask: 2, qm1, 3 | 1 |
| 5 | console.log('4') | Microtasks pending: 2, qm1, 3 | 1, 4 |
| 6 | (microtask) run 2 | queueMicrotask: 2.qm; remaining: qm1, 3, 2.qm | 1, 4, 2 |
| 7 | (microtask) run qm1 | remaining: 3, 2.qm | 1, 4, 2, qm1 |
| 8 | (microtask) run 3 | remaining: 2.qm | 1, 4, 2, qm1, 3 |
| 9 | (microtask) run 2.qm | — | 1, 4, 2, qm1, 3, 2.qm |
Step-by-Step Explanation
Step 1 — console.log('1')
Runs immediately.
Prints 1.
✅ Console: 1
Step 2 — First Promise.resolve().then(...)
- Schedules microtask 2.
Step 3 — queueMicrotask(() => console.log('qm1'))
- Schedules microtask qm1 after
2.
Step 4 — Second Promise.resolve().then(...)
- Schedules microtask 3 after
2,qm1.
Step 5 — console.log('4')
- Prints 4.
✅ Console: 1, 4
Step 6 — Microtask: run 2
Prints 2.
Inside it: schedules microtask 2.qm.
Queue becomes:
qm1,3,2.qm.
✅ Console: 1, 4, 2
Step 7 — Microtask: run qm1
- Prints qm1.
✅ Console: 1, 4, 2, qm1
Step 8 — Microtask: run 3
- Prints 3.
✅ Console: 1, 4, 2, qm1, 3
Step 9 — Microtask: run 2.qm
- Prints 2.qm.
✅ Final Console:1, 4, 2, qm1, 3, 2.qm
(async () => {
console.log('t1');
await Promise.resolve().then(() => console.log('inner'));
console.log('t2');
})();
Promise.resolve().then(() => console.log('p'));
console.log('sync');
// t1
// sync
// inner
// p
// t2
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | IIFE call | — | — |
| 2 | console.log('t1') | — | t1 |
| 3 | await Promise.resolve().then(...) | Microtask: inner | t1 |
| 4 | Promise.resolve().then(() => console.log('p')) | Microtask queued: p | t1 |
| 5 | console.log('sync') | Microtasks: inner, then p, then resume → t2 | t1, sync |
| 6 | (microtask) inner | Microtasks left: p, resume async | t1, sync, inner |
| 7 | (microtask) p | Microtasks left: resume async | t1, sync, inner, p |
| 8 | (microtask) resume async → console.log('t2') | — | t1, sync, inner, p, t2 |
Step-by-Step Explanation
Step 1 — IIFE runs
- The async IIFE begins executing immediately.
Step 2 — console.log('t1')
Runs synchronously.
Prints t1.
✅ Console: t1
Step 3 — await Promise.resolve().then(() => console.log('inner'))
The
.then()schedules the microtask inner.awaitpauses the async function.Remaining part (
t2) goes to the microtask queue (after inner).
Step 4 — Promise.resolve().then(() => console.log('p'))
- Schedules microtask p.
Step 5 — console.log('sync')
Runs synchronously.
Prints sync.
✅ Console: t1, sync
Step 6 — Microtask: inner
- Prints inner.
✅ Console: t1, sync, inner
Step 7 — Microtask: p
- Prints p.
✅ Console: t1, sync, inner, p
Step 8 — Resume async function → console.log('t2')
The async function continues after the final await.
Prints t2.
✅ Final Console: t1, sync, inner, p, t2
console.log('start');
Promise.resolve().then(() => {
console.log('p1');
return new Promise(res => res('v'));
}).then(v => console.log('then', v));
console.log('end');
// start
// end
// p1
// then v
Execution Table
| Step | Call Stack | Web APIs / Queues | Console Output |
| 1 | console.log('start') | — | start |
| 2 | Promise.resolve().then(...) | Microtask queued: p1 | start |
| 3 | console.log('end') | Microtasks pending: p1 | start, end |
| 4 | (microtask) run p1 | New Promise created → resolves → microtask queued to continue chain with value "v" | start, end, p1 |
| 5 | (microtask) run chained .then(v => console.log('then', v)) | — | start, end, p1, then v |
Step-by-Step Explanation
Step 1 — console.log('start')
Runs synchronously.
Prints start.
✅ Console: start
Step 2 — Promise.resolve().then(...)
- Schedules microtask p1.
Step 3 — console.log('end')
Runs synchronously.
Prints end.
✅ Console: start, end
Step 4 — Microtask: run p1
Prints p1.
Returns a new Promise whose executor resolves immediately (
res('v')).Because it resolves instantly, the chained
.then(...)is added as a new microtask.
✅ Console: start, end, p1
Step 5 — Microtask: chained .then(v => console.log('then', v))
- Receives
"v"and prints:
then v
✅ Final Console: start, end, p1, then v
Too long for revision? Here is a pattern structure to start thinking about any questions.
PATTERN 1 — Basic Execution + setTimeout + Promise
console.log("A");
setTimeout(() => console.log("T1"));
Promise.resolve().then(() => console.log("P1"));
console.log("B");
PATTERN 2 — Promise Chain Ordering
console.log("A");
Promise.resolve()
.then(() => console.log("P1"))
.then(() => console.log("P2"));
console.log("B");
PATTERN 3 — Promise Inside setTimeout
setTimeout(() => {
console.log("T1");
Promise.resolve().then(() => console.log("P1"));
});
console.log("A");
PATTERN 4 — Async/Await With Microtasks
async function f() {
console.log("F1");
await null;
console.log("F2");
}
console.log("A");
f();
console.log("B");
PATTERN 5 — Async + Promise Mix
async function f() {
console.log("F1");
await Promise.resolve().then(() => console.log("P1"));
console.log("F2");
}
console.log("A");
f();
console.log("B");
PATTERN 6 — Multiple Awaits & Interleaving
async function f() {
console.log("F1");
await 0;
console.log("F2");
await 0;
console.log("F3");
}
console.log("A");
f();
console.log("B");
PATTERN 7 — Nested Promises
console.log("A");
Promise.resolve().then(() => {
console.log("P1");
Promise.resolve().then(() => console.log("P2"));
});
console.log("B");
PATTERN 8 — Nested setTimeout + Promise
setTimeout(() => {
console.log("T1");
Promise.resolve().then(() => console.log("P1"));
setTimeout(() => console.log("T2"));
});
console.log("A");
PATTERN 9 — Async Inside Timeout
setTimeout(async () => {
console.log("T1");
await 0;
console.log("A1");
});
console.log("A");
PATTERN 10 — Microtask Explosion (Important)
Promise.resolve().then(() => {
console.log("P1");
return Promise.resolve().then(() => console.log("P2"));
}).then(() => console.log("P3"));
console.log("A");
PATTERN 11 — Interwoven Async Functions
async function x() {
console.log("X1");
await y();
console.log("X2");
}
async function y() {
console.log("Y1");
await null;
console.log("Y2");
}
console.log("A");
x();
console.log("B");
PATTERN 12 — The Full Combo (Everything Together)
console.log("S");
setTimeout(() => console.log("T1"));
async function f() {
console.log("F1");
await Promise.resolve().then(() => console.log("P1"));
console.log("F2");
}
f();
Promise.resolve().then(() => console.log("P2"));
setTimeout(() => {
console.log("T2");
Promise.resolve().then(() => console.log("P3"));
});
console.log("E");
For answers checkout my github link - https://github.com/vaishdwivedi1/js-async
Do share any other questions you find interesting!



