Giới thiệu

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 giản khi viết code vì không phải lo về các vấn đề khi chạy song song (Ví dụ luồng chính phải đợi các luồng con trả về kết quả để tổng kết).

Bạn đang xem: Synchronous là gì

Giờ thì bạn hãy tưởng tượng client gửi request lấy dữ liệu từ một API. Ở đây có thể xảy trường hợp server có thể mất thời gian để xử lý request (Hoặc tệ hơn là server không trả về kết quả) nếu ở đây đợi đến khi server trả về kết quả mới chạy tiếp thì nó sẽ khiến trang web không phản hồi.

Vậy Javascript mới tạo ra Asynchronous để 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.Thôi không dài dòng nữa bây giờ chúng ta hãy bắt đầu về Synchronous và Asynchronous nào.

Javascript Synchronous hoạt động như thế nào?

Bây giờ chúng ta có 1 đoạn code như sau:

const second = function() { console.log(“Hello there!”);}const first = function() { console.log(“Hi there!”); second(); console.log(“The End”);}first();Các bạn hãy dự đoán kết quả sẽ in ra như thế nào?Vâng và đây là kết quả, các bạn cùng xem nhé:

*

Javascript thực thi lệnh theo thứ tự main -> first() -> console.log(“Hi there!”) -> second() -> console.log(“Hello there!”)- > (Kết thúc second) -> console.log(“The End”) -> (Kết thúc first) -> (Kết thúc main). Với main ở đây là luồng chạy của chương trình. Và để chương trình chạy được như thế thì cần đến cái gọi là call stack.

Call stack: Đúng như tên gọi nó là ngăn xếp chứa các lệnh được thực thi. Với nguyên tắc LIFO (Last In First Out – Vào sau thì ra trước). Và vì Javascript là ngôn ngữ đơn luồng nên chỉ có 1 call stack này để thực thi lệnh.Chúng ta có thể mô tả lại quá trình chạy lệnh trên theo sơ đồ sau:

*

Vậy đấy chính là cách mà Javascript Synchronous thực hiện

Javascript Asynchronous hoạt động như thế nào?

Chúng ta có đoạn code sau để minh họa cho Javascript Asynchronous:

const networkRequest = function() { setTimeout(function timer() { console.log(“Async Code”); }, 2000);};console.log(“Hello World”);networkRequest();console.log(“The End”);Mình xin giải thích. Ở đây networkRequest có sử dụng setTimeout để giả lập cho hành động gửi 1 request đến API. Và đây là kết quả

*

Để giải thích cho javascript asynchronous chúng ta cần biết thêm về Event loop, web APIs và Message queue. Mình xin lưu ý là đây không phải là của javascript mà nó là 1 phần của trình biên dịch javascript của browser.

Event loop:Nhiệm vụ của Event loop là xem trong call stack có trống hay không. Nếu như nó trống thì sẽ xem tiếp trong message queue có callback nào đang đợi không. Nếu có thì nó sẽ đẩy callback đó vào call stack.Message queue: Hay còn gọi là Task queue, Callback queue. Đây là hàng đợi của các task khi sử dụng Web APIs. Và các task sẽ được lấy ra theo quy tắc FIFO (First In First Out – Vào trước ra trước) và sẽ được event loop đưa lên call stack để thực thi.

Xem thêm: Frame Là Gì

Web APIs: Bao gồm setTimeout, DOM Events (Ví dụ sự kiện click button, …), …

Sau khi đã biết được những khái niệm trên mình xin giải thích lại khối code ở trên (Sẽ rất khó hiểu đây

*

Tới đây nếu bạn đã thực sự hiểu thì xin chúc mừng bạn. Còn nếu bạn vẫn chưa hiểu thì hãy xem lại ví dụ ở link này Click vào đây

P/s: Có thể chỉnh tốc độ chậm lại để dễ quan sát hơn.

ES6 Job Queue/ Micro-Task queue

ES6 đã giới thiệu khái niệm job queue/micro-task queue được Promise sử dụng. Sự khác biệt giữa message queuejob queuejob queue có mức độ ưu tiên cao hơn message queue, điều đó có nghĩa là các công việc trong job queue/micro-task queue sẽ được thực hiện trước message queue.

Chúng ta hãy xem ví dụ dưới đây:

console.log(“Script start”); setTimeout(() => { console.log(“setTimeout”); }, 0); new Promise((resolve, reject) => { resolve(“Promise resolved”); }).then(res => console.log(res)) .catch(err => console.log(err)); console.log(“Script End”);Bạn dự đoán kết quả thử rồi hãy xem kết quả nhé. Và đây là kết quả

Script startScript EndPromise resolvedsetTimeoutChúng ta thấy rằng Promise được thực hiện trước setTimeout vì callback ở Promise được lưu bên trong job queue/micro-task queue có mức độ ưu tiên cao hơn message queue.

Ví dụ tiếp theo

console.log(“Script start”); setTimeout(() => { console.log(“setTimeout 1”);}, 0);setTimeout(() => { console.log(“setTimeout 2”);}, 0);new Promise((resolve, reject) => { resolve(“Promise 1 resolved”);}).then(res => console.log(res)) .catch(err => console.log(err)); new Promise((resolve, reject) => { resolve(“Promise 2 resolved”);}).then(res => console.log(res)) .catch(err => console.log(err));console.log(“Script End”);Kết quả:

Script startScript EndPromise 1 resolvedPromise 2 resolvedsetTimeout 1setTimeout 2Kết quả cũng tương tự như trên là Promise thực hiện trước setTimeout. Event loop sẽ thực hiện hết callback trong job queue/micro-task queue trước rồi mới đến message queue.

Xem thêm: Inversion Of Control Là Gì, Inversion Of Control, Di Là Gì

Kết luận

Chúng ta đã tìm hiểu cách JavaScript Synchronous và JavaScript Asynchronous hoạt động và các khái niệm như call stack, event loop, message queue/task queuejob queue/micro-task queue. Hy vọng bài viết này giúp ích được cho các bạn

*

Chúc các bạn học tập và công tác tốt.

Tài liệu tham khảo: https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff

Chuyên mục: Hỏi Đáp