Event Loop & Call Stack in JavaScript - Exercise 2
The task queue holds callbacks from operations such as timers and DOM events until the event loop can run them. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
setTimeout(() => console.log('task one'), 0);
button.addEventListener('click', () => console.log('event task'));A microtask is high-priority asynchronous work, commonly created by Promise handlers or queueMicrotask. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
queueMicrotask(() => console.log('microtask'));
Promise.resolve().then(() => console.log('promise microtask'));The Promise handler runs first because the event loop drains microtasks before taking the next task. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
setTimeout(() => console.log('timer'), 0);
Promise.resolve().then(() => console.log('promise'));
// promise, timerJavaScript runs every queued microtask before moving to the next task, including microtasks added by earlier microtasks. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
queueMicrotask(() => {
console.log('first');
queueMicrotask(() => console.log('second'));
});
setTimeout(() => console.log('timer'), 0);The code after await pauses and resumes as a Promise microtask, while synchronous code outside the async function can continue. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
async function load() {
console.log('before await');
await Promise.resolve();
console.log('after await');
}
load();
console.log('outside');
// before await, outside, after awaitThe delay is a minimum wait. The callback still waits for the current stack and earlier queued work to finish. The example makes the scheduling boundary visible without relying on implementation-specific timing.
const start = Date.now();
while (Date.now() - start < 100) {}
setTimeout(() => console.log('runs after the blocking loop'), 0);Each fulfilled handler schedules the next handler as another microtask. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
Promise.resolve()
.then(() => console.log('step one'))
.then(() => console.log('step two'));
setTimeout(() => console.log('task'), 0);Synchronous code runs first, then microtasks, then timer or other task callbacks. This ordering explains why asynchronous work cannot interrupt the synchronous code that is already running.
console.log('sync');
Promise.resolve().then(() => console.log('microtask'));
setTimeout(() => console.log('task'), 0);
// sync, microtask, taskPromise reactions are microtasks, and runtimes drain the microtask queue after the current stack before taking the next task such as a timer.
Promise.resolve().then(() => console.log('promise'));
setTimeout(() => console.log('timer'), 0);
// promise, timerEach microtask can queue another microtask, so a long chain keeps the runtime draining that queue before it returns to timer or rendering work.
let count = 0;
function queueNext() {
if (count++ < 2) queueMicrotask(queueNext);
}
queueNext();