Trước hết xác định bản chất: event loop là một thread, một hàm CPU chạy 2 giây thì mọi request khác chờ đúng 2 giây. Không có cách nào "async" hóa việc CPU thuần — chỉ có chia nhỏ hoặc đưa ra khỏi thread chính.
Bốn hướng và điều kiện dùng:
- Partitioning — cắt việc thành từng phần nhỏ, nhường event loop bằng
setImmediategiữa các phần. Chỉ hợp khi việc chia được và tổng thời gian ngắn; độ trễ tổng tăng nhưng API không đứng. worker_threads— chạy JS trên thread khác trong cùng tiến trình, chia sẻ được bộ nhớ quaSharedArrayBuffer. Đây là lựa chọn mặc định cho CPU-bound trong cùng service: khởi tạo rẻ hơn process, truyền dữ liệu nhanh hơn. Nên dùng pool cố định (piscinahoặc tự viết) thay vì tạo worker mỗi request, vì tạo worker tốn vài chục ms.cluster— fork nhiều tiến trình cùng chia sẻ port để tận dụng nhiều core. Nó tăng throughput tổng thể, nhưng một request nặng vẫn chặn cả worker process đó. Cluster là công cụ mở rộng theo core, không phải cách sửa CPU-bound.child_process— khi cần chạy chương trình ngoài (ffmpeg, LibreOffice) hoặc cần cách ly hoàn toàn (crash không kéo theo tiến trình chính).- Job queue ngoài (BullMQ/Redis, SQS) — khi tác vụ dài (giây tới phút) hoặc cần retry, ưu tiên, quan sát tiến độ. API trả
202 Acceptedkèm job id, client poll hoặc nhận webhook.
Quyết định cho hai ví dụ trong câu hỏi: resize ảnh vài trăm ms → worker pool. Xuất Excel 200k dòng → job queue, sinh file rồi trả link tải; kèm streaming khi ghi file để không giữ toàn bộ trong RAM.