🚦 Single-Threaded là gì?
- Thread là một đơn vị làm việc, giống như một công nhân có thể thực hiện các nhiệm vụ.
- JavaScript chỉ có một công nhân (một thread).
- Công nhân này có thể thực hiện chỉ một việc tại một thời điểm.
Điều này có nghĩa là nếu JS đang chạy một đoạn mã, nó không thể bắt đầu một đoạn mã khác cùng lúc trên một thread khác.
🛑 Tại sao điều này quan trọng?
- Nếu một nhiệm vụ là chậm (ví dụ: một vòng lặp nặng hoặc chờ một tệp tin), công nhân đơn lẻ sẽ bị kẹt.
- Điều này có thể làm cho toàn bộ ứng dụng của bạn (như một trang web) đóng băng cho đến khi nhiệm vụ hoàn tất.
🍕 Phân tích tương tự
Hãy tưởng tượng một đầu bếp pizza trong một nhà hàng nhỏ:
- Chỉ có một đầu bếp (single thread).
- Nếu đầu bếp đang bận làm pizza, anh ta không thể làm mì cùng lúc.
- Để xử lý thời gian chờ (như pizza đang nướng trong lò), đầu bếp có thể đặt một cái hẹn giờ và chuyển sang đơn hàng tiếp theo.
- Cái hẹn giờ đó giống như callback, promise hoặc async/await.
⚡ Làm thế nào mà JS vẫn cảm thấy nhanh?
JavaScript sử dụng event loop + các thao tác bất đồng bộ:
- Các nhiệm vụ chậm (cuộc gọi mạng, hẹn giờ, v.v.) được chuyển cho trợ lý nền của trình duyệt / Node.js.
- Khi chúng hoàn tất, chúng thông báo “Này, tôi đã sẵn sàng” và thread đơn lẻ sẽ xử lý nó sau.
Điều này khiến JS cảm giác như thể nó đang đa nhiệm, mặc dù chỉ có một việc được thực hiện tại một thời điểm.
💻 Ví dụ mã
javascript
console.log("Bắt đầu");
setTimeout(() => {
console.log("Nhiệm vụ bất đồng bộ hoàn thành");
}, 0);
console.log("Kết thúc");
Kết quả
javascript
Bắt đầu
Kết thúc
Nhiệm vụ bất đồng bộ hoàn thành
👉 Dù thời gian chờ là 0ms, callback được trì hoãn cho đến khi thread chính hoàn tất các nhiệm vụ hiện tại (Bắt đầu và Kết thúc).
Điều này chứng minh rằng JavaScript chỉ có một thread chính.
✅ Tóm tắt
JavaScript là single-threaded = một công nhân chính thực hiện các nhiệm vụ lần lượt.
Với event loop + async, nó vẫn có thể xử lý nhiều nhiệm vụ một cách mượt mà mà không bị chặn.
Các thực tiễn tốt nhất
- Sử dụng Async/Await: Giúp mã rõ ràng và dễ đọc hơn.
- Tránh các hoạt động nặng trong thread chính: Sử dụng Web Workers cho các tác vụ tốn thời gian.
- Sử dụng Promise: Để thực hiện các tác vụ bất đồng bộ mà không làm chậm trải nghiệm người dùng.
- Tối ưu hóa vòng lặp: Đảm bảo các vòng lặp không hiệu quả không gây kẹt cho thread chính.
Những cạm bẫy thường gặp
- Không hiểu rõ về cách hoạt động của event loop có thể dẫn đến việc viết mã kém hiệu quả.
- Không xử lý các lỗi trong các callback có thể làm cho ứng dụng bị treo hoặc không phản hồi.
Mẹo tối ưu hiệu suất
- Sử dụng setTimeout với thời gian nhỏ nhất cần thiết để kích hoạt callback sau khi hoàn tất công việc.
- Tránh các tác vụ đồng bộ nặng: Sử dụng các API bất đồng bộ để không làm tắc nghẽn thread chính.
Giải quyết sự cố
- Nếu bạn gặp phải tình trạng ứng dụng không phản hồi, hãy kiểm tra các mã bất đồng bộ của bạn để đảm bảo không có thao tác nào gây tắc nghẽn.
- Sử dụng công cụ phát triển của trình duyệt để theo dõi hiệu suất và tìm các điểm nghẽn.
Câu hỏi thường gặp
JavaScript có thể chạy đa luồng không?
JavaScript chủ yếu chạy trên một thread, nhưng có thể tận dụng Web Workers để thực hiện các tác vụ nặng trong nền mà không ảnh hưởng đến thread chính.
Event loop là gì?
Event loop là cơ chế của JavaScript cho phép nó xử lý các tác vụ bất đồng bộ, đảm bảo rằng mã được thực thi theo thứ tự mà không bị chặn.
Async/Await có khác gì so với Promise?
Async/Await là cú pháp giúp làm cho mã sử dụng Promise dễ đọc và dễ hiểu hơn, tương tự như mã đồng bộ.