Asynchronous Programming và Performance
Dưới đây là phần giải thích chuyên sâu về Asynchronous Programming và Performance trong Node.js theo đúng góc nhìn của một Senior Backend Developer:
1. EventEmitter trong Node.js là gì và cách sử dụng nó?
✅ EventEmitter là gì?
EventEmitter
là một lớp cốt lõi trong module events
của Node.js, cho phép các đối tượng phát và lắng nghe các sự kiện tùy chỉnh — tương tự như pattern Publish/Subscribe.
✅ Cách sử dụng:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
// Đăng ký sự kiện
myEmitter.on('greeting', (name) => {
console.log(`Hello, ${name}!`);
});
// Phát sự kiện
myEmitter.emit('greeting', 'Tài');
✅ Ứng dụng:
Xử lý kết nối socket.
Giao tiếp giữa các module.
Tích hợp vào các lớp tùy chỉnh.
2. Cách xử lý nhiều yêu cầu bất đồng bộ trong Node.js
✅ Sử dụng các kỹ thuật không đồng bộ:
Callback
Promise
Async/Await
Event Loop
✅ Sử dụng Promise.all()
cho chạy song song:
Promise.all()
cho chạy song song:const fs = require('fs').promises;
async function readFiles() {
const [a, b] = await Promise.all([
fs.readFile('a.txt'),
fs.readFile('b.txt')
]);
console.log(a.toString(), b.toString());
}
✅ Không block luồng chính:
Node.js đơn luồng (single-threaded) nhưng xử lý I/O bất đồng bộ qua Event Loop và libuv threadpool.
Tránh các hàm
fs.readFileSync()
hoặcJSON.parse()
trên dữ liệu lớn trong main thread.
3. Sự khác biệt giữa setTimeout
và setImmediate
setTimeout
và setImmediate
setTimeout(fn, 0)
setImmediate(fn)
Thời điểm thực thi
Sau ít nhất 0ms (qua Timer phase)
Ngay sau I/O events (Check phase)
Ưu tiên
Có thể bị delay tùy lịch của Event Loop
Gần như được chạy sớm hơn
Dùng khi nào?
Khi cần trì hoãn cụ thể
Khi muốn chạy ngay sau I/O
✅ Ví dụ:
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
// Kết quả tùy hệ thống, nhưng thường:
// immediate
// timeout
4. Làm thế nào để tối ưu hóa hiệu suất trong ứng dụng Node.js?
✅ Một số kỹ thuật tối ưu hóa:
🔸 1. Tránh block Event Loop
Không xử lý logic CPU nặng trên main thread.
Dùng
worker_threads
hoặc chia task sang microservice khác.
🔸 2. Dùng cache (in-memory hoặc Redis)
Tránh gọi DB nhiều lần với cùng dữ liệu.
Dùng
node-cache
,lru-cache
hoặc Redis.
🔸 3. Sử dụng cluster hoặc worker_threads
// cluster example
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
for (let i = 0; i < os.cpus().length; i++) cluster.fork();
} else {
require('./server'); // hoặc app.listen()
}
🔸 4. Sử dụng Async/Await
thay vì callback
Code dễ đọc và bảo trì hơn.
Tránh "callback hell".
🔸 5. Sử dụng Streaming thay vì load toàn bộ dữ liệu
const fs = require('fs');
fs.createReadStream('large-file.txt')
.pipe(res); // tránh memory overload
🔸 6. Giảm số lượng request/response
Dùng compression, HTTP2, pagination hoặc GraphQL.
🔸 7. Monitor và Benchmark
Sử dụng APM như PM2 + Keymetrics, New Relic, hoặc Datadog.
Phân tích memory leak, CPU profiling.
✅ Tổng kết nhanh:
Khái niệm
Mô tả
EventEmitter
Lớp phát và lắng nghe sự kiện tùy chỉnh
Async Handling
Dùng Promise/async-await/Promise.all để chạy song song
setTimeout vs setImmediate
Khác nhau về thời điểm ưu tiên trong Event Loop
Tối ưu hiệu suất
Cache, cluster, stream, tránh blocking, monitoring
Last updated