JavaScript là ngôn ngữ lập trình Single-thread (đơn luồng), có nghĩa là tại 1 thời điểm chỉ có thể xử lý 1 lệnh nên việc đợi 1 task nào đó mà mất vài chục giây hoặc vài phút (điển hình như lấy dữ liệu từ API) để xử lý thì quả thật là rất tệ.
Vậy nên Javascript mới tạo ra Asynchronous (bất đồng bộ) để giúp chúng ta làm việc này (như callbacks, Promises, async/await) giúp luồng chạy của web không bị chặn lại khi đợi request.
Ví dụ về bất đồng bộ
Mình có một ví dụ nhỏ dưới đây
console.log(1, 'start') let sum = 0 for (let i = 0; i < 100; i++) { sum += i } console.log(2, sum) console.log(3, 'end') /* ======== RESULT ========= 1 start 2 4950 3 end */
nó sẽ log ra thứ tự từ trên xuống bởi vì ví dụ trên là code đồng bộ (synchronous)
Chúng ta đến với một ví dụ nữa
console.log(1, 'start') setTimeout(() => { console.log(2, 'first setTimeOut') }, 1000); setTimeout(() => { console.log(3, 'second setTimeOut') }, 2000); console.log(4, 'end') /* ======== RESULT ========= 1 start 4 end 2 first setTimeOut 3 second setTimeOut */
Nó không hiển thị ra thứ tự từ trên xuống mà lần này nó hiện theo một trình tự khác.
Như vậy là bất đồng bộ (asynchronous) đã xảy ra khi không hiển thị đúng như thứ tự từ trên xuống.
Để hiểu rõ và chi tiết hơn, chúng ta sẽ đi từng phần nhé.
Hiểu về STACK và QUEUE trong bất đồng bộ
STACK đúng như cái tên của nó là ngăn xếp, đây là nơi thực thi các đoạn mã code đồng bộ , với nguyên tắc LIFO (Last In First Out – vào sau thì ra trước)
Nếu như STACK nhận phải đoạn code bất đồng bộ thì lập tức đoạn code đó sẽ chuyển qua Web APIs để xử lý.
Web APIs hoặc I/O Network: Đây là nơi xử lý thời gian của các đoạn code bất đồng bộ (ví dụ như khi gọi API). Sau khi xử lý xong thì đoạn code sẽ được đẩy vào MESSAGE QUEUE.
MESAGE QUEUE :Đây là hàm đợi của các đoạn code bất đồng bộ đã được xử lý thời gian, với nguyên tắc FIFO (First In First Out – Vào trước ra trước)
Các đoạn code trong MESSAGE QUEUE sẽ được Event loop đẩy lên STACK để thực thi nếu như lúc đó STACK đang trống.
Trong MESSAGE QUEUE được phân chia làm 2 loại :
- MISCOTASK QUEUE :Đây là ngăn xếp mà Event loop sẽ luôn được ưu tiên đẩy các đoạn code trong này lên STACK, gồm có các hàm như
process.nextTick()
(trong nodejs),queueMicrotask()
, async function, Promise callback,… - MACROTASK QUEUE: Đây là ngăn xếp bình thường, sẽ được Event loop đẩy lên STACK theo thứ tự.
Đó là cơ chế hoạt động của bất đồng bộ (asynchronous) trong Javascript. Mình có ví dụ cuối cùng trong bài này
setTimeout(() => { console.log("start") }) setTimeout(() => { console.log('time out after 1s') }, 1000); setTimeout(() => { console.log('time out after 2s') }, 2000); queueMicrotask(() => { console.log('queueMicrotask!') }) Promise.resolve().then(() => { console.log('Promise') }) console.log('end')
Các bạn thử đoán xem nó sẽ thực thi như nào và bình luận ở phía dưới nhé.
Cảm ơn các bạn đã đọc bài viết.
Bài viêt tham khảo medium.com