Node.js - Vòng Lặp Sự Kiện: Llifting Bí Ẩn đằng Sau JavaScript Tương Tự

Xin chào các nhà lập trình tương lai! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào trái tim của Node.js - Vòng Lặp Sự Kiện (Event Loop). Đừng lo lắng nếu bạn chưa từng viết một dòng mã trước đây; tôi sẽ là hướng dẫn viên thân thiện của bạn trong thế giới kỳ diệu này. Cuối cùng của bài hướng dẫn này, bạn sẽ hiểu cách Node.js quản lý để làm nhiều việc cùng một lúc, giống như bạn xử lý bài tập về nhà, xem Netflix và nhắn tin cho bạn bè!

Node.js - Event Loop

Vòng Lặp Sự Kiện là gì?

Hãy tưởng tượng bạn là một đầu bếp trong nhà hàng忙 rộn. Bạn có nhiều món ăn đang nấu, các计时器 đang tick tick, và các đơn hàng liên tục đến. Bạn làm thế nào để quản lý tất cả mà không để thức ăn bị cháy hoặc khách hàng phải chờ đợi? Đó chính xác là điều mà Vòng Lặp Sự Kiện làm cho Node.js!

Vòng Lặp Sự Kiện giống như một đầu bếp chính, liên tục kiểm tra điều gì cần chú ý và đảm bảo mọi thứ chạy mượt mà. Đó là bí quyết giúp Node.js thực hiện các thao tác I/O không chặn, mặc dù JavaScript là một luồng đơn.

Các Khái Niệm Chính

Trước khi chúng ta đi sâu hơn, hãy làm quen với một số khái niệm chính:

  1. Single-threaded: JavaScript chạy trên một luồng duy nhất, có nghĩa là nó chỉ có thể làm một việc tại một thời điểm.
  2. Non-blocking: Node.js có thể xử lý nhiều thao tác mà không chờ đợi từng thao tác hoàn thành trước khi chuyển sang cái tiếp theo.
  3. Asynchronous: Các nhiệm vụ có thể được bắt đầu bây giờ và hoàn thành sau, cho phép mã khác chạy trong thời gian chờ.

Vòng Lặp Sự Kiện hoạt động như thế nào?

Hãy phân chia Vòng Lặp Sự Kiện thành các bước dễ tiêu hóa:

  1. Thực hiện mã đồng bộ trong ngăn xếp gọi hàm (call stack)
  2. Kiểm tra các计时器 (setTimeout, setInterval)
  3. Kiểm tra các thao tác I/O chờ xử lý
  4. Thực hiện các callback của setImmediate
  5. Xử lý các sự kiện 'close'

Bây giờ, hãy xem điều này trong hành động với một số ví dụ mã!

Ví dụ 1: Mã đồng bộ vs. mã bất đồng bộ

console.log("First");

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

console.log("Third");

Bạn nghĩ đầu ra sẽ là gì? Hãy phân tích:

  1. "First" được ghi lại ngay lập tức.
  2. setTimeout gặp phải, nhưng thay vì chờ đợi, Node.js thiết lập một计时器 và tiếp tục.
  3. "Third" được ghi lại.
  4. Vòng Lặp Sự Kiện kiểm tra các计时器 đã hoàn thành và thực hiện callback, ghi lại "Second".

Đầu ra:

First
Third
Second

Ngạc nhiên? Điều này minh họa cách Node.js xử lý các thao tác bất đồng bộ mà không chặn luồng chính.

Ví dụ 2: Nhiều计时器

setTimeout(() => console.log("Timer 1"), 0);
setTimeout(() => console.log("Timer 2"), 0);
setTimeout(() => console.log("Timer 3"), 0);

console.log("Hello from the main thread!");

Trong ví dụ này, chúng ta đang thiết lập nhiều计时器 với độ trễ 0 miligiây. Tuy nhiên, Vòng Lặp Sự Kiện vẫn sẽ xử lý chúng sau khi luồng chính hoàn thành.

Đầu ra:

Hello from the main thread!
Timer 1
Timer 2
Timer 3

Các Phase của Vòng Lặp Sự Kiện

Bây giờ chúng ta đã thấy Vòng Lặp Sự Kiện trong hành động, hãy khám phá các phase của nó chi tiết hơn:

1. Phase Timers

Phase này thực hiện các callback được lịch trình bởi setTimeout() và setInterval().

setTimeout(() => console.log("I'm a timer!"), 100);
setInterval(() => console.log("I repeat every 1 second"), 1000);

2. Phase Pending Callbacks

Ở phase này, vòng lặp thực hiện các callback I/O bị hoãn đến vòng lặp tiếp theo.

3. Phase Idle, Prepare

Chỉ sử dụng nội bộ. Không có gì để xem ở đây, các bạn!

4. Phase Poll

Lấy các sự kiện I/O mới và thực hiện các callback liên quan đến I/O.

const fs = require('fs');

fs.readFile('example.txt', (err, data) => {
if (err) throw err;
console.log(data);
});

5. Phase Check

setImmediate() callbacks được gọi ở phase này.

setImmediate(() => console.log("I'm immediate!"));

6. Phase Close Callbacks

Một số callback 'close', ví dụ như socket.on('close', ...), được xử lý ở phase này.

Kết hợp tất cả lại

Hãy tạo một ví dụ phức tạp hơn sử dụng các khía cạnh khác nhau của Vòng Lặp Sự Kiện:

const fs = require('fs');

console.log("Start");

setTimeout(() => console.log("Timeout 1"), 0);
setImmediate(() => console.log("Immediate 1"));

fs.readFile('example.txt', (err, data) => {
console.log("File read complete");
setTimeout(() => console.log("Timeout 2"), 0);
setImmediate(() => console.log("Immediate 2"));
});

console.log("End");

Thứ tự thực thi có thể làm bạn ngạc nhiên:

  1. "Start" và "End" được ghi lại ngay lập tức.
  2. Các setTimeout đầu tiên và setImmediate được排队.
  3. Thao tác đọc file bắt đầu.
  4. Vòng Lặp Sự Kiện bắt đầu các chu kỳ của mình:
  • Callback của setTimeout đầu tiên được thực hiện.
  • Callback của setImmediate đầu tiên được thực hiện.
  • Khi thao tác đọc file hoàn thành, callback của nó được thực hiện.
  • Trong callback của thao tác đọc file, một setTimeout và setImmediate khác được排队.
  • setImmediate thứ hai được thực hiện trước setImmediate thứ hai.

Các phương thức thường gặp của Vòng Lặp Sự Kiện

Dưới đây là bảng các phương thức liên quan đến Vòng Lặp Sự Kiện phổ biến trong Node.js:

Phương thức Mô tả
setTimeout(callback, delay) Thực hiện callback sau delay milliseconds
setInterval(callback, interval) Thực hiện callback liên tục mỗi interval milliseconds
setImmediate(callback) Thực hiện callback trong vòng lặp tiếp theo của Event Loop
process.nextTick(callback) Thêm callback vào ngăn xếp "next tick" được xử lý sau khi thao tác hiện tại hoàn thành

Kết luận

Chúc mừng! Bạn vừa bước vào thế giới kỳ diệu của Node.js và Vòng Lặp Sự Kiện. Nhớ rằng, giống như học骑自行车, việc thành thạo lập trình bất đồng bộ cần thời gian để thực hành. Đừng nản lòng nếu nó không ngay lập tức hiểu rõ - hãy tiếp tục thử nghiệm và sớm bạn sẽ viết mã không chặn như một chuyên gia!

Khi chúng ta kết thúc, đây là một ví dụ vui: hãy tưởng tượng Vòng Lặp Sự Kiện như một vòng carousel. Các nhiệm vụ khác nhau (như timers, thao tác I/O và callbacks tức thì) giống như những đứa trẻ cố gắng nhảy lên. Vòng Lặp Sự Kiện liên tục quay, picking up và dropping off các nhiệm vụ theo thứ tự cụ thể, đảm bảo mọi người đều có lượt mà không làm dừng vòng carousel.

Tiếp tục lập mã, 保持好奇心, và nhớ - trong thế giới của Node.js, kiên nhẫn không chỉ là một đức tính, nó là một callback!

Credits: Image by storyset