Trong phần đầu tiên, chúng ta đã tìm hiểu về khái niệm của xử lí đồng bộ (Sync) và bất đồng bộ (Async), cũng như bản chất thực sự của các xử lí bên trong Javascript. Trong phần tiếp theo này, chúng ta sẽ xem xét tới những hàm “bất đồng bộ” như setTimeout() và xử lí AJAX.
Khi bạn không muốn Mr. X xử lí ngay yêu cầu của bạn
Quay trở lại với Mr. X thần thánh và cách ông ta nhận yêu cầu thông qua các lời nhắn được chuyển tới từ Mr. M. Cũng nhớ lại rằng, Mr. X chỉ có thể giải quyết 1 vấn đề tại 1 thời điểm xác định.
Giả sử, bạn gởi một yêu cầu tới Mr. X bằng một tin nhắn thông qua Mr. M, chúng ta hãy xem xét tới thời điểm Mr. X bắt đầu xử lí yêu cầu của bạn. Yêu cầu của bạn cho Mr. X như sau: “sau 1 khoảng thời gian (ví dụ 5 giây), hãy giúp tôi xử lí việc ABC”, câu hỏi là: Mr. X có “đứng im” không làm gì cả trong 5 giây, và chỉ khi sau 5 giây mới thực hiện việc ABC cho bạn hay không? Câu trả lời là: KHÔNG.
Vậy Mr. X xử lí thế nào? Câu trả lời hợp lí là: Khi gặp 1 yêu cầu có dạng “sau xxx giây, thực hiện việc yyy” thì Mr. X sẽ nhờ tới 1 anh giúp việc khác (gọi là Mr. H – H nghĩa là Help). Công việc của Mr. H là: sau đúng xxx giây như yêu cầu, Mr.H sẽ gửi yêu cầu xử lí việc yyy vào hàng đợi tin nhắn (message queue) mà Mr. M đang quản lí, bởi chỉ có Mr. M mới có thẩm quyền đưa yêu cầu tới cho Mr. X mà thôi. Như vậy, yêu cầu xử lí ban đầu được đưa về đúng dạng “tin nhắn xử lí thông thường” với sự giúp đỡ của Mr. H.
Cơ chế thực thi của hàm setTimeout()
Tương tự như các xử lí của Mr. X và đồng sự trình bày ở phần trên, hàm setTimeout() được dùng khi bạn muốn yêu cầu của mình chỉ được xử lí sau 1 khoảng thời gian nhất định. Bạn sẽ truyền hàm xử lí của bạn vào setTimeout() với format sau:
setTimeout(function, delay-time, arguments ...);
Tham số function là hàm xử lí của bạn, delay-time là thời gian trì hoãn được tính bằng milisecond, và arguments là các tham số khác nếu cần truyền vào.
Chạy thử một ví dụ thực tế sau đây. Yêu cầu chính của bạn là: in ra dòng chữ “Test setTimeout” sau 1 giây trì hoãn kể từ khi nhận yêu cầu. Vòng lặp for ở phía dưới để đảm bảo hàng đợi tin nhắn vẫn luôn có các yêu cầu mới.
setTimeout(function () { console.log("Test setTimeout"); }, 1000); for (var i = 0; i < 10000; i++) { console.log("Test For"); }
Về mặt logic, hàm setTimeout() được kích hoạt trước vòng lặp for, tức là nó được gửi tới message queue trước. Khi chạy đoạn code này, ta sẽ thấy các dòng chữ “Test For” được in ra trước và ngay lập tức, xử lí in ra “test setTimeout” không được xử lí ngay vì có delay-time là 1000 milisecond (1 giây), điều đó chứng tỏ: khi gặp lệnh setTimeout(), Mr.X vẫn tiếp tục xử lí các yêu cầu khác.
Sau 1 giây, yêu cầu in ra “Test setTimeout” được đưa vào message queue, nhưng nó sẽ không được thực thi ngay vì lúc này Call-stack đang bị chiếm bởi nó chưa xử lí xong yêu cầu in ra “Test For”, chỉ khi nào Call-stack xử lí xong công việc hiện tại, thì Event-loop mới lấy yêu cầu mới từ message queue để xử lí tiếp. Đó là lí do vì sao dòng chữ “test setTimeout” có thể mất lâu hơn 1 giây để được in ra màn hình. Kết quả thử nghiệm được thể hiện qua hình sau:

Cơ chế và trình tự chạy khi gọi AJAX
Kĩ thuật AJAX (Asynchronous Javascript And XML) là một kĩ thuật sử dụng các XMLHttpRequest (XHR) API để gửi request tới server và quản lí các response.
Thông thường, khi gửi một request tới server, thì trang web đang hiển thị sẽ được load lại toàn bộ để hiển thị nội dung mới. Bằng các sử dụng AJAX, trang web vẫn có thể hiển thị nội dung mới mà không cần phải tải lại toàn bộ trang.
Mặc định, khi gửi một request bởi XHR thì code xử lí đang ở dạng “bất đồng bộ”, có nghĩa là code vẫn tiếp tục chạy mà không cần đợi phản hồi từ server. Tuy nhiên ta vẫn có thể cấu hình để XHR có thể chạy “đồng bộ”, tức là sẽ cần phải nhận được phản hồi từ server thì code phía sau mới có thể chạy tiếp được. XHR API sẽ giúp ta quản lí việc nạp các request mới vào message queue theo đúng cách ta đã chỉ định.
//Mở request dạng Asynchronous var xhr = new XMLHttpRequest(); xhr.open("Get", "https://www.example.com"); //Mở request dạng Synchronous var xhr = new XMLHttpRequest(); xhr.open("Get", "https://www.example.com", false);
Bạn đã hiểu cơ chế thực thi trong Javascript rồi chứ
Hầu hết mọi người khi tìm hiểu Javascript đều sẽ bối rối khi phải phân biệt giữa xử lí đồng bộ (Synchronous) và bất đồng bộ (Asynchronous), cũng như bản chất thực sự khi thực thi của các xử lí này. Bằng việc tìm hiểu cơ chế thực thi thông qua hàng đợi tin nhắn (message queue) ẩn bên trong khi gọi hàm trong Javascript, chúng ta sẽ có một cái nhìn chi tiết và sẽ đỡ bối rối hơn trong việc hiểu và kiểm soát các mã lệnh được viết ra.
Với việc phân tích các hàm “bất đồng bộ” như setTimeout() và AJAX, chúng ta đã hiểu được tham số delay-time trong setTimeout() không phải là thời gian chờ để thực thi, mà chính là thời gian chờ để xử lí được đưa vào message queue. Bên cạnh đó, việc giao tiếp với server thông qua các XMLHttpRequest cũng không hẳn lúc nào cũng là Async, chúng ta vẫn có thể viết code đồng bộ (Sync) như thường.
Chúc vui 🙂
Vcttai
Tham khảo:
- Understanding Synchronous and Asynchronous in JavaScript
- What is the difference between synchronous and asynchronous programming (in node.js)
Chào bạn, mình có câu hỏi này: theo trong bài khi xử lý bất đồng bộ javascript cần đến mr H. mr H ở đây có phải là 1 luồng trong hệ thống? Và javascript cần nhiều hơn 1 luồng để xử lý bất đồng bộ? Nếu vậy thì bản chất singlethread của javascript là sai?
LikeLike
Trong một frame function1() khi gặp setTimeout( args, time) thì mình nghĩ setTimout đó sẽ được đẩy vào Callback Queue (có thể là Mr. H) trong tình huống này.
LikeLike
lau lam moi doc 1 bai viet hay va ro rang nhu vay
LikeLike
Cảm ơn bạn 😀
LikeLike
Hay thật cảm ơn bạn nhiều
LikeLike
Pingback: Hiểu rõ cơ chế xử lí đồng bộ và bất đồng bộ (Sync vs Async) trong JavaScript – P2 - Trang Chủ