DEV Community

Satish
Satish

Posted on

JavaScript Event Loop Explained: Macrotasks, Microtasks, and Async/Await Made Simple

Event Loop:

The mechanism that controls how JavaScript handles asynchronous operations. It continuously checks the call stack and task queues to decide what code runs next.

- Macrotask:

A scheduled task that runs after all microtasks are completed. Examples include setTimeout, setInterval, and DOM events.

- Microtask:

A smaller, high‑priority task that runs immediately after the current synchronous code finishes. Examples include Promise.then, queueMicrotask, and async/await continuations.

- queueMicrotask():
A built‑in JavaScript method that adds a function to the microtask queue, ensuring it runs before any macrotask.

- Async/Await:

Syntax built on top of Promises that makes asynchronous code look synchronous. The code after await runs as a microtask once the Promise resolves.

console.log("Start");

setTimeout(() => {
  console.log("Macrotask: setTimeout");
}, 0);

queueMicrotask(() => {
  console.log("Microtask: queueMicrotask");
});

Promise.resolve().then(() => {
  console.log("Microtask: Promise.then");
});

async function demo() {
  console.log("Async function start");
  await null; // resolves immediately
  console.log("Microtask: async/await continuation");
}
demo();

console.log("End");
Enter fullscreen mode Exit fullscreen mode

Output:

Start
Async function start
End
Microtask: queueMicrotask
Microtask: Promise.then
Microtask: async/await continuation
Macrotask: setTimeout
Enter fullscreen mode Exit fullscreen mode

Break down of code:
➡️ Prints "Start" immediately. This is synchronous code, so it runs first.
➡️ Schedules a macrotask. Even with 0 delay, it won’t run right now.
It goes into the macrotask queue and will only run after all microtasks are finished.
➡️ Schedules a microtask. This will run right after the current synchronous code finishes, before any macrotask.
➡️ Creates a resolved Promise. The .then callback is added to the microtask queue. So it will run after synchronous code, but still before macrotasks.
➡️ Defines and calls an async function:

  • Prints "Async function start" immediately (synchronous part of the function).
  • await null pauses execution. Since null resolves instantly, the continuation (console.log("Microtask: async/await continuation")) is scheduled as a microtask.
  • So that line runs later, in the microtask phase.

➡️ Prints "End" immediately. Still synchronous code.

Top comments (0)