ducdev
Backend / Bài viết

Node.js Event Loop — Tại Sao Một Thread Xử Lý Được Hàng Nghìn Request

Node.js chạy trên một thread duy nhất nhưng phục vụ hàng nghìn concurrent connections. Hiểu Event Loop giúp bạn tránh được những bugs khó tìm nhất trong Node.js.

a
admin
14/05/2026 · 3 phút đọc · 0 lượt xem
Chia sẻ

Khi lần đầu nghe "Node.js chạy single-threaded", nhiều người thắc mắc: vậy làm sao nó handle được 10.000 requests cùng lúc? Câu trả lời nằm ở Event Loop — một trong những thiết kế thông minh nhất trong software engineering.

Mô Hình Truyền Thống Vs Node.js

Server truyền thống (Apache, Django với WSGI):

  • Mỗi request → một thread riêng
  • Thread chờ I/O (DB, file) → bị block
  • 1000 concurrent requests → 1000 threads → tốn RAM

Node.js:

  • Một thread, non-blocking I/O
  • Khi chờ I/O → event loop làm việc khác
  • 1000 concurrent requests → vẫn một thread

Event Loop — Cơ Chế Hoạt Động

// Mỗi "tick" của Event Loop:
// 1. Xử lý hết callback trong call stack
// 2. Microtasks (Promise, queueMicrotask)
// 3. Macrotasks (setTimeout, setInterval, I/O callbacks)

console.log('1');  // Sync — chạy ngay

setTimeout(() => console.log('2'), 0);  // Macrotask — chạy sau

Promise.resolve().then(() => console.log('3'));  // Microtask — chạy trước macrotask

console.log('4');  // Sync — chạy ngay

// Output: 1, 4, 3, 2

Tại Sao Non-blocking?

const fs = require('fs');

// BLOCKING — block toàn bộ event loop trong khi đọc file!
const data = fs.readFileSync('large-file.txt');
// Trong lúc này, KHÔNG request nào khác được xử lý

// NON-BLOCKING — đăng ký callback và tiếp tục ngay
fs.readFile('large-file.txt', (err, data) => {
    // Callback này chỉ chạy khi file đã đọc xong
    console.log(data.length);
});
// Event loop tiếp tục xử lý requests khác trong khi chờ file

Phases Của Event Loop

// Event Loop chạy theo vòng, mỗi vòng có nhiều phases:
//
// timers → I/O callbacks → idle/prepare → poll → check → close callbacks
//
// poll phase: Chờ I/O events (file read, network) — đây là phần tốn thời gian nhất
// timers: Chạy setTimeout và setInterval đã đến hạn
// check: Chạy setImmediate callbacks

setImmediate(() => console.log('setImmediate'));
setTimeout(() => console.log('setTimeout 0'), 0);
// Thứ tự phụ thuộc vào phase hiện tại

Blocking The Event Loop — Kẻ Thù Số Một

// XẤU — CPU-intensive block event loop
app.get('/api/compute', (req, res) => {
    // Tính toán nặng chạy synchronously
    let result = 0;
    for (let i = 0; i < 1e9; i++) result += i;
    res.json({ result });
    // Trong 5 giây này, KHÔNG request nào khác được xử lý!
});

// TỐT — đẩy vào Worker Thread
const { Worker } = require('worker_threads');

app.get('/api/compute', (req, res) => {
    const worker = new Worker('./compute-worker.js');
    worker.on('message', result => res.json({ result }));
});

Async/Await Và Event Loop

// async/await là syntactic sugar cho Promise
// Bên dưới vẫn dùng Event Loop

async function handleRequest(req, res) {
    // Đây là non-blocking — event loop tiếp tục trong khi chờ DB
    const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);

    // Parallel I/O — chạy cùng lúc, không sequential
    const [posts, followers] = await Promise.all([
        db.query('SELECT * FROM posts WHERE user_id = ?', [user.id]),
        db.query('SELECT * FROM followers WHERE user_id = ?', [user.id]),
    ]);

    res.json({ user, posts, followers });
}
Node.js không phải lúc nào cũng tốt hơn Python hay Java. Nó tỏa sáng với I/O-bound workloads (API gateway, real-time apps). Với CPU-bound (video encoding, ML), Go hoặc Python với multiprocessing phù hợp hơn.

Kết Luận

Hiểu Event Loop giúp bạn tránh được bug phổ biến nhất với Node.js: vô tình block event loop bằng synchronous operations. Quy tắc đơn giản: mọi I/O đều phải async, mọi CPU-intensive task phải ra Worker Thread.

#Node.js #JavaScript #backend #Event Loop #Async
a
Tác giả
admin

Lập trình viên, yêu thích chia sẻ kiến thức về công nghệ và phát triển phần mềm.

Bình luận

Chưa có bình luận. Hãy là người đầu tiên!

Để lại bình luận

Bình luận sẽ được duyệt trước khi hiển thị.