Mastering the JavaScript Event Loop and Concurrency Model
Jul 29, 2025 am 04:01 AMJavaScript is single-threaded but handles concurrency through the event loop, which coordinates the call stack, callback queue (task queue), microtask queue, and Web APIs; when asynchronous operations complete, their callbacks are placed in either the task queue (e.g., setTimeout) or microtask queue (e.g., Promise.then); the event loop processes all microtasks before each task, explaining why Promise callbacks run before setTimeout even with zero delay; this leads to output order 1, 4, 3, 2 in the example; microtask abuse can block the UI since rendering waits for the microtask queue to clear; Node.js adds complexity with process.nextTick(), which runs before other microtasks, making it higher priority than Promises; understanding these mechanics helps avoid timing bugs and performance issues.
JavaScript’s event loop and concurrency model are foundational to understanding how the language handles asynchronous operations, even though it runs on a single thread. Despite being single-threaded, JavaScript can perform non-blocking I/O and handle multiple tasks over time—thanks to its event loop and runtime environment (like Node.js or the browser). Let’s break this down clearly and practically.

How JavaScript Handles Concurrency with a Single Thread
At its core, JavaScript executes code in a single call stack—meaning only one operation runs at a time. But if it's single-threaded, how does it manage to handle asynchronous tasks like timers, HTTP requests, or user events without freezing the entire application?
The answer lies in its concurrency model, which relies on:

- The call stack
- The callback queue (or task queue)
- The microtask queue
- The event loop
These components work together with the help of Web APIs (in browsers) or C bindings (in Node.js) to offload asynchronous operations.
For example:

console.log("1"); setTimeout(() => { console.log("2"); }, 0); Promise.resolve().then(() => { console.log("3"); }); console.log("4");
You might expect the output to be 1, 2, 3, 4
, but it’s actually:
1 4 3 2
Why? Because of how the event loop prioritizes different types of callbacks.
The Event Loop in Action: Task vs Microtask
When asynchronous operations complete, their callbacks are placed in different queues:
- Task Queue (Callback Queue): Holds callbacks from
setTimeout
,setInterval
, I/O, DOM events, etc. - Microtask Queue: Holds callbacks from
Promise.then/catch/finally
,queueMicrotask
, andMutationObserver
.
The event loop checks these queues in a specific order:
- Run all synchronous code (call stack).
- After the stack is empty, process all microtasks (in order).
- Then, pick one task from the task queue and run it.
- Repeat.
This means microtasks always run before any new tasks, even if they were added after a timer.
Back to our earlier example:
console.log("1"); // Sync setTimeout(() => console.log("2")); // Task Promise.resolve().then(() => console.log("3")); // Microtask console.log("4"); // Sync
Execution flow:
- Logs "1"
setTimeout
→ goes to Web API, later enqueues task- Promise resolved → microtask queued
- Logs "4"
- Call stack empty → event loop checks microtask queue → runs "3"
- Then pulls from task queue → runs "2"
Hence: 1, 4, 3, 2
Common Pitfalls and Practical Implications
Understanding the difference between microtasks and tasks helps avoid performance issues and logical bugs.
1. Microtask Abuse Can Block the UI
Since microtasks run before the next task (and before rendering), flooding the microtask queue can delay rendering and user interactions.
let count = 0; function recurse() { count ; console.log(count); Promise.resolve().then(recurse); } recurse();
This creates an infinite chain of microtasks—the browser can't render, respond to clicks, or run timers until the microtask queue is drained. It effectively blocks the UI.
2. Order Matters in Async Code
Mixing setTimeout
, Promise
, and queueMicrotask
can lead to subtle timing bugs:
setTimeout(() => console.log("timeout"), 0); queueMicrotask(() => console.log("micro1")); Promise.resolve().then(() => console.log("promise")); queueMicrotask(() => console.log("micro2"));
Output:
micro1 promise micro2 timeout
All microtasks execute before the timeout callback, regardless of when they were scheduled.
Node.js vs Browser: Slight Differences
While the core model is the same, Node.js has additional quirks:
process.nextTick()
: A special microtask that runs before other microtasks (even Promises).
Promise.resolve().then(() => console.log("promise")); process.nextTick(() => console.log("nextTick"));
Output in Node.js:
nextTick promise
So nextTick
has higher priority than Promise microtasks—use it carefully.
Key Takeaways
- JavaScript is single-threaded but uses an event loop to handle concurrency.
- The event loop continuously checks:
- Call stack (run sync code)
- Microtask queue (run all)
- Task queue (run one)
-
Microtasks (Promises,
queueMicrotask
) run before tasks (timers, I/O). - Avoid infinite microtask loops—they block the event loop.
- In Node.js,
process.nextTick()
has higher priority than Promise microtasks.
Understanding this model helps debug timing issues, optimize performance, and write more predictable async code.
Basically, the event loop isn’t magic—it’s just a loop that keeps your app responsive by carefully managing what runs when.
The above is the detailed content of Mastering the JavaScript Event Loop and Concurrency Model. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

1. Why use asynchronous programming? Traditional programming uses blocking I/O, which means that the program waits for an operation to complete before continuing. This may work well for a single task, but may cause the program to slow down when processing a large number of tasks. Asynchronous programming breaks the limitations of traditional blocking I/O and uses non-blocking I/O, which means that the program can distribute tasks to different threads or event loops for execution without waiting for the task to complete. This allows the program to handle multiple tasks simultaneously, improving the program's performance and efficiency. 2. The basis of Python asynchronous programming The basis of Python asynchronous programming is coroutines and event loops. Coroutines are functions that allow a function to switch between suspending and resuming. The event loop is responsible for scheduling
![nodejs visual learning: event loop [animation demonstration]](https://img.php.cn/upload/article/000/000/024/6380baaf3077a109.jpg?x-oss-process=image/resize,m_fill,h_207,w_330)
This article will help you learn the Node event loop through animation. I hope it will be helpful to you!

Asynchronous programming, English Asynchronous Programming, means that certain tasks in the program can be executed concurrently without waiting for other tasks to complete, thereby improving the overall operating efficiency of the program. In Python, the asyncio module is the main tool for implementing asynchronous programming. It provides coroutines, event loops, and other components required for asynchronous programming. Coroutine: Coroutine is a special function that can be suspended and then resumed execution, just like a thread, but a coroutine is more lightweight and consumes less memory than a thread. The coroutine is declared with the async keyword and execution is suspended at the await keyword. Event loop: Event loop (EventLoop) is the key to asynchronous programming

As web applications grow and become more complex, event-driven programming has become a common choice among PHP programmers. The event loop mechanism in PHP programs allows the program to handle multiple concurrent requests asynchronously, thereby improving performance and scalability. However, using the event loop mechanism correctly requires adopting best practices to ensure program stability and maintainability. This article will discuss event loop best practices in PHP programs. Use the right event library PHP has many different event libraries to choose from, such as ReactPH

Introduction Asynchronous programming is becoming increasingly popular in modern computing. It is a programming paradigm that allows applications to handle multiple tasks simultaneously, thereby increasing efficiency and maximizing the use of computer resources. pythonasyncio is a library designed for asynchronous programming that provides a wide range of features and tools to enable developers to easily write high-performance and scalable applications. Coroutines and event loops The core concepts of asyncio are coroutines and event loops. Coroutines are a cooperative multitasking mechanism that allow a function to relinquish control while suspending execution and waiting for an event to occur. The event loop is an infinite loop that monitors events and schedules coroutines as needed. The following demo code shows a simple coroutine: importasyn

Yes, event loops are very important in modern PHP development, especially when building real-time or high-concurrency systems. Event loops are the core mechanism of asynchronous programming, allowing PHP to handle multiple tasks without waiting for each operation to complete. ReactPHP and Swoole implement event loops in different ways: ReactPHP adopts a Node.js-style callback model, suitable for small asynchronous tools; Swoole embeds optimized event loops and supports coroutines, which facilitates integration with existing frameworks. Using event loops can improve resource utilization, achieve low latency and real-time functions, but it is necessary to avoid blocking functions, pay attention to shared state risks, and perform load testing.

As web applications become more complex, the need for consistently high concurrency and low latency increases. This means that the traditional request-response programming model can no longer meet the needs. At this time, asynchronous programming and event-driven programming have become very important tools, and Swoole provides support for these two programming models. This article will introduce Swoole's event loop mechanism and how to implement it. What is an event loop? The event loop is an I/O model that uses the event notification mechanism provided by the operating system to wait for and process events.

Python asynchronous programming is a powerful technology that can achieve high concurrency and high performance programs. It achieves concurrency by using coroutines and event loops, thereby avoiding the locking and synchronization issues in traditional multi-threaded programming. Coroutine: A coroutine is a function that can pause and resume execution. When a coroutine is suspended, it saves its state in memory and relinquishes control to another coroutine. When another coroutine has finished executing, the suspended coroutine can resume execution from where it last stopped. Event loop: The event loop is a continuously looping function that obtains events from the operating system and then distributes these events to the corresponding coroutines. When a coroutine needs to wait for an event, it can register itself with the event loop. When an event occurs,
