blense.
HomeFrontendCodeAINewsGuidesCheatsheetsAbout

Technology decoded, always.

A curated read on AI, product, and the culture behind the code. No noise, no hype.

blense.

Technology with focus. Stories about what innovation really changes.

Sections

FrontendArtificial IntelligenceCode & DevTech News

Blense

AboutRSSPrivacy policyTermsCookies
© 2026 Blense · blense.fun
Code & Development

JavaScript Event Loop: The Heart of Non-Blocking Concurrency — Revisited in 2025

The Event Loop is not just a simple loop, but a sophisticated coordination mechanism between Call Stack, queues, and runtime. Essential for JS concurrency, it enables thousands of asynchronous operations without blocking the UI. This guide explores its structure, differences between Node.js and browsers, common bugs, advanced techniques, and 2025 trends like Structured Concurrency and scheduler.yield().

Gby Genildo SouzaJul 266 min read
JavaScript Event Loop: The Heart of Non-Blocking Concurrency — Revisited in 2025

The event loop is the fundamental mechanism that allows JavaScript to execute non-blocking asynchronous operations. In 2025, with Node.js 22 and more sophisticated engines, understanding how the event loop works is essential for developers seeking real performance in modern applications.

Why the Event Loop Still Matters in 2025?

If you develop with JavaScript — whether on the frontend with React, Vue, or Svelte, or on the backend with Node.js, Bun, or Deno — the event loop is present in every asynchronous operation you execute. Data fetching, timers, promises, DOM manipulation, stream processing: everything goes through this mechanism.

The critical point: most developers know the event loop exists, but do not understand how it actually works. And this superficial knowledge has a high cost in 2025.

With the popularization of real-time applications, WebSockets, Server-Sent Events, and serverless architectures, the event loop has become the most common bottleneck in high-performance applications. Understanding its nuances is no longer a "bonus skill" — it is an essential competency.

What Is the Event Loop: Fundamentals

The event loop is the concurrency mechanism that allows JavaScript to execute non-blocking operations despite being, by nature, a single-threaded language. This apparent contradiction is resolved through an intelligent model of queues and execution cycles.

JAVASCRIPT
console.log('1. Início do script');setTimeout(() => {  console.log('2. Timeout executado');}, 0);Promise.resolve().then(() => {  console.log('3. Promise microtask');});console.log('4. Fim do script');// Output:// 1. Início do script// 4. Fim do script// 3. Promise microtask// 2. Timeout executado

The Queue Architecture

The event loop operates with multiple execution queues, each with distinct priority and behavior:

JAVASCRIPT
// Macrotask queuesetTimeout(() => console.log('Macrotask 1'), 0);setImmediate(() => console.log('Immediate 1')); // Node.js apenas// Microtask queuePromise.resolve().then(() => console.log('Microtask 1'));queueMicrotask(() => console.log('Microtask via queueMicrotask'));// process.nextTick (Node.js - executa antes das microtasks)process.nextTick(() => console.log('nextTick'));// Output em Node.js:// nextTick// Microtask 1// Microtask via queueMicrotask// Macrotask 1// Immediate 1

Evolution of the Event Loop: From 2015 to 2025

The Landscape in 2015

In 2015, the event loop was relatively simple. V8 was in its 4.x version, and native Promise support was recent (ES6). Most developers still used callbacks, and async/await was a novelty on the horizon.

The basic model was:

  • Task Queue: where setTimeout, I/O callbacks, and UI events entered

  • Microtask Queue: introduced with Promises, but rarely used

The Revolution of 2017-2019

With the standardization of async/await (ES2017) and the introduction of queueMicrotask in the specification, the behavior of microtasks became more predictable. V8 optimized Promise handling throughout its versions, reducing the overhead of object creation.

JAVASCRIPT
async function fetchData(urls) {  const results = await Promise.all(    urls.map(url => fetch(url).then(r => r.json()))  );  return results;}

The State of the Art in 2025

In 2025, the ecosystem around the event loop has matured:

  1. Mature worker threads: Real parallelism for CPU-bound tasks

  2. Async context tracking: Asynchronous context tracking via AsyncLocalStorage

  3. Bun and Deno: New runtimes with optimized implementations

  4. scheduler.yield(): Native API to yield control to the event loop (Chrome 124+)

JAVASCRIPT
// AsyncLocalStorage — disponível desde Node.js 16, amplamente adotado em 2025import { AsyncLocalStorage } from 'async_hooks';const requestStorage = new AsyncLocalStorage();function handleRequest(req, res) {  requestStorage.run({ requestId: generateId() }, () => {    logRequest(req);    processData();  });}async function processData() {  const store = requestStorage.getStore();  console.log(`Processing request: ${store.requestId}`);}

Microtasks vs Macrotasks: The Conflict of Priorities

JAVASCRIPT
console.log('=== Início ===');setTimeout(() => console.log('1. setTimeout (macrotask)'), 0);Promise.resolve().then(() => console.log('2. Promise.then (microtask)'));queueMicrotask(() => console.log('3. queueMicrotask (microtask)'));if (typeof process !== 'undefined' && process.nextTick) {  process.nextTick(() => console.log('4. nextTick (Node.js only)'));}console.log('=== Fim do script ===');// Output:// === Início ===// === Fim do script ===// 4. nextTick (se Node.js)// 2. Promise.then// 3. queueMicrotask// 1. setTimeout

Why Does This Matter?

The order of execution directly affects:

  • Rendering guarantees: Browsers render between macrotasks

  • Event loop blocking: Long microtasks block everything

  • Order of state updates: React and other frameworks depend on this behavior

JAVASCRIPT
// PROBLEMA: loop pesado dentro de microtask bloqueia o event loop inteiroasync function processarDados() {  const dados = await buscarDados();  for (let i = 0; i < 10000000; i++) {    processarItem(dados[i]);  }}// SOLUÇÃO: dividir em chunks cedendo o controle entre cada umasync function processarDadosOtimizado(dados) {  for (let i = 0; i < dados.length; i += 1000) {    const chunk = dados.slice(i, i + 1000);    chunk.forEach(processarItem);    await new Promise(resolve => setTimeout(resolve, 0));  }}

Benchmarks: Measuring the Event Loop

The values below are references to guide decisions — run them in your own environment for accurate results.

JAVASCRIPT
import { performance } from 'perf_hooks';function benchmark(name, fn, iterations = 100000) {  const start = performance.now();  for (let i = 0; i < iterations; i++) {    fn();  }  const end = performance.now();  console.log(`${name}: ${((end - start) / iterations * 1000).toFixed(3)}µs por operação`);}benchmark('Promise.then', () => {  Promise.resolve().then(() => {});});benchmark('queueMicrotask', () => {  queueMicrotask(() => {});});
Operation

Promise.then / queueMicrotask

setTimeout(0)

Async function overhead

Order of magnitude

Fractions of a microsecond

1–5ms (minimum imposed by the runtime)

Similar to Promise.then

ℹ️

The practical difference: setTimeout(0) is orders of magnitude slower than microtasks for internal scheduling. Never use setTimeout(0) to coordinate state updates.

Node.js vs Browser: Divergent Event Loops

Node.js Event Loop (libuv)

TEXT
┌───────────────────────────┐│  Timers                   │  setTimeout, setInterval└─────────────┬─────────────┘              ▼┌───────────────────────────┐│  Pending callbacks        │  erros de I/O da iteração anterior└─────────────┬─────────────┘              ▼┌───────────────────────────┐│  Poll                     │  aguarda I/O, executa callbacks└─────────────┬─────────────┘              ▼┌───────────────────────────┐│  Check                    │  setImmediate└─────────────┬─────────────┘              ▼┌───────────────────────────┐│  Close callbacks          │  socket.on('close', ...)└───────────────────────────┘Entre CADA fase, microtasks são executadas.

Browser Event Loop

JAVASCRIPT
// No browser, o modelo é mais simples:// 1. Execute todo o JavaScript síncrono// 2. Execute todas as microtasks// 3. Render (se necessário)// 4. Execute próxima macrotask// requestAnimationFrame — executa antes do paintrequestAnimationFrame(() => {  console.log('Antes do render');});// requestIdleCallback — para trabalho não urgenterequestIdleCallback(() => {  console.log('Browser está ocioso');});

Anti-Patterns and Best Practices in 2025

Error #1: Confusing setTimeout(0) with setImmediate

JAVASCRIPT
// ❌ Assumir que são equivalentessetTimeout(() => console.log('timeout'), 0);setImmediate(() => console.log('immediate'));// A ordem pode variar em Node.js dependendo do contexto de I/O.// setImmediate não existe no browser — use setTimeout(0) lá.

Error #2: Blocking the event loop with large volumes

JAVASCRIPT
// ❌ Processa tudo de uma vezasync function importUsers(users) {  for (const user of users) {    await saveUser(user);  }}// ✅ Processa em batches cedendo o controle entre cada umasync function importUsersBatched(users, batchSize = 100) {  for (let i = 0; i < users.length; i += batchSize) {    const batch = users.slice(i, i + batchSize);    await Promise.all(batch.map(saveUser));    await new Promise(resolve => setTimeout(resolve, 0));    reportProgress(Math.min(i + batchSize, users.length), users.length);  }}

Error #3: Not using AbortController for cancellation

JAVASCRIPT
// ✅ Cancelamento limpo com AbortControllerasync function fetchWithCancel(url, signal) {  const response = await fetch(url, { signal });  return response.json();}const controller = new AbortController();fetchWithCancel('/api/data', controller.signal)  .then(data => console.log(data))  .catch(err => {    if (err.name === 'AbortError') {      console.log('Request cancelado');    }  });setTimeout(() => controller.abort(), 5000);

Conclusion

The event loop is not an implementation detail — it is the JavaScript execution model. Ignoring how it works means writing code with timing bugs waiting to appear in production.

The elite dev's arsenal.
Stop using forEach: 8 semantic array methods to make your JavaScript code readable8 minSolidJS in 2026: Why fine-grained reactivity is outperforming the Virtual DOM27 minTanStack Query: How to Eliminate Manual Server State Management in React6 minStop Using useMemo Wrong: A Practical Guide to React Performance14 min
  • ✅ Microtasks for atomic state updates

  • ✅ Chunks + yield for processing large volumes

  • ✅ requestIdleCallback for non-urgent tasks in the browser

  • ✅ AbortController for clean cancellation

  • ✅ Worker threads for real CPU-bound work

🔗 References

  1. MDN — Execution model

  2. Node.js Docs — The Node.js Event Loop

  3. V8 Blog — v8.dev/blog

  4. HTML Spec — Event Loop Processing Model

Takeaway: "In JavaScript, time is not linear — it is a queue of tasks, microtasks, and callbacks. Whoever understands the queue, understands time."

Keep exploring
Want more content like this?

Check out other articles in the same vein and keep the momentum.

See more articles
#JavaScript#Node.js

The elite dev's arsenal.

Microfrontends with Angular and Nx: what nobody tells you
Code & Development

Microfrontends with Angular and Nx: what nobody tells you

Most tutorials show how to build microfrontends in minutes, but few reveal the architectural debt that emerges after months of production deployments.

Genildo Souza · Jul 28 · 15 min
Next.js 16 PPR: Eliminate the static vs. dynamic tradeoff and achieve Edge TTFB with fresh data
Code & Development

Next.js 16 PPR: Eliminate the static vs. dynamic tradeoff and achieve Edge TTFB with fresh data

Partial Prerendering in Next.js 16 resolves the classic rendering tradeoff, delivering static speed with dynamic data in one seamless response.

Genildo Souza · Jul 13 · 14 min
Stop using forEach: 8 semantic array methods to make your JavaScript code readable
Code & Development

Stop using forEach: 8 semantic array methods to make your JavaScript code readable

forEach is too generic. Methods like map, filter, and reduce declare code intent before the body, aiding readability. Learn when to use each one.

Genildo Souza · Jul 12 · 8 min
In this article
  • Why the Event Loop Still Matters in 2025?
  • What Is the Event Loop: Fundamentals
  • The Queue Architecture
  • Evolution of the Event Loop: From 2015 to 2025
  • The Landscape in 2015
  • The Revolution of 2017-2019
  • The State of the Art in 2025
  • Microtasks vs Macrotasks: The Conflict of Priorities
  • Why Does This Matter?
  • Benchmarks: Measuring the Event Loop
  • Node.js vs Browser: Divergent Event Loops
  • Node.js Event Loop (libuv)
  • Browser Event Loop
  • Anti-Patterns and Best Practices in 2025
  • Error #1: Confusing setTimeout(0) with setImmediate
  • Error #2: Blocking the event loop with large volumes
  • Error #3: Not using AbortController for cancellation
  • Conclusion
  • 🔗 References