Frontend Development 11 min read

Mastering JavaScript Event Loop: Microtasks, Macrotasks, and Async Tricks

This article thoroughly explains JavaScript's single‑threaded nature and Event Loop, detailing how synchronous and asynchronous tasks are queued as macro‑tasks or micro‑tasks, and demonstrates their behavior with code examples, diagrams, and analysis of setTimeout, setInterval, Promise and process.nextTick.

MaoDou Frontend Team
MaoDou Frontend Team
MaoDou Frontend Team
Mastering JavaScript Event Loop: Microtasks, Macrotasks, and Async Tricks

Based on a previous article about microtasks and macrotasks, this piece provides a detailed supplement on JavaScript's event loop.

Key points

JavaScript is a single‑threaded language.

The Event Loop is the execution mechanism of JavaScript.

JavaScript event loop illustration

JS executes functions one after another; a long‑running task blocks subsequent tasks. To avoid blocking, developers split work into synchronous tasks (e.g., page skeleton rendering) and asynchronous tasks (e.g., loading large images or audio).

The diagram shows that each method call adds an execution context to the call stack, which can call other methods, creating a potentially infinite nesting until a stack overflow occurs.

When a page loads, rendering the skeleton and elements are synchronous tasks, while loading heavy resources such as images or audio are asynchronous tasks.

Code example

Print order:

script start, script end, promise1, promise2, setTimeout

Why does this order appear?

Synchronous and asynchronous tasks enter different queues: synchronous tasks run on the main thread, while asynchronous tasks register in the Event Table and later move to the Event Queue when ready.

Macro‑tasks: whole script, setTimeout, setInterval, setImmediate. Micro‑tasks: native Promise, process.nextTick, MutationObserver, etc.

setTimeout

setTimeout schedules a function to be placed in the Event Queue after the specified delay. If the main thread is busy, the actual delay can be longer than requested.

<code>// Execute console
// task()</code>

Even

setTimeout(fn, 0)

does not execute immediately; it runs after the call stack is empty, at the earliest possible idle moment.

setInterval

setInterval repeatedly registers a function in the Event Queue at the given interval. If a previous task takes longer than the interval, the next execution is delayed.

Promise and process.nextTick

Promise callbacks are micro‑tasks that run after the current synchronous code finishes, before any macro‑tasks.

process.nextTick

in Node.js behaves similarly to a zero‑delay setTimeout.

Example output: Promise1, Promise2, setTimeout1

Micro‑tasks are cleared before the macro‑task queue is processed, which explains the observed order.

Complex example analysis

A multi‑step script demonstrates three rounds of the event loop, showing how macro‑tasks (setTimeout) and micro‑tasks (Promise, process.nextTick) interleave, producing the final output sequence:

1, 7, 6, 8, 2, 4, 3, 5, 9, 11, 10, 12

The analysis includes tables of macro‑task and micro‑task queues after each round.

Conclusion

Understanding the distinction between macro‑tasks and micro‑tasks and how the Event Loop schedules them is essential for writing efficient, non‑blocking JavaScript code.

JavaScriptasyncevent-loopPromiseMicrotaskssetTimeoutmacrotasks
MaoDou Frontend Team
Written by

MaoDou Frontend Team

Open-source, innovative, collaborative, win‑win – sharing frontend tech and shaping its future.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.