Định nghĩa
Thuật ngữ "không đồng bộ" có thể được sử dụng theo các nghĩa hơi khác nhau, dẫn đến các câu trả lời dường như mâu thuẫn ở đây, trong khi chúng thực sự không. Wikipedia về không đồng bộ có định nghĩa này:
Không đồng bộ, trong lập trình máy tính, đề cập đến sự xuất hiện của các sự kiện độc lập với luồng chương trình chính và các cách để đối phó với các sự kiện đó. Đây có thể là các sự kiện "bên ngoài" như sự xuất hiện của tín hiệu hoặc hành động do chương trình khởi xướng đồng thời với việc thực hiện chương trình mà không có chương trình chặn để chờ kết quả.
mã không phải JavaScript có thể xếp hàng các sự kiện "bên ngoài" như vậy vào một số hàng đợi sự kiện của JavaScript. Nhưng đó là xa như nó đi.
Không có quyền ưu tiên
Không có sự gián đoạn bên ngoài của việc chạy mã JavaScript để thực thi một số mã JavaScript khác trong tập lệnh của bạn. Các mảnh JavaScript được thực hiện lần lượt từng thứ tự và thứ tự được xác định theo thứ tự các sự kiện trong mỗi hàng đợi sự kiện và mức độ ưu tiên của các hàng đợi đó.
Chẳng hạn, bạn có thể hoàn toàn chắc chắn rằng sẽ không có JavaScript nào khác (trong cùng một tập lệnh) thực thi trong khi đoạn mã sau đang thực thi:
let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
sum += a[i];
}
Nói cách khác, không có quyền ưu tiên trong JavaScript. Bất cứ điều gì có thể có trong hàng đợi sự kiện, việc xử lý các sự kiện đó sẽ phải đợi cho đến khi đoạn mã đó chạy đến khi hoàn thành. Đặc tả EcmaScript cho biết trong phần 8.4 Công việc và Hàng đợi Công việc :
Việc thực thi một công việc chỉ có thể được bắt đầu khi không có bối cảnh thực thi đang chạy và ngăn xếp bối cảnh thực thi trống.
Ví dụ về sự không đồng bộ
Như những người khác đã viết, có một số tình huống trong đó tính không đồng bộ xuất hiện trong JavaScript và nó luôn liên quan đến hàng đợi sự kiện, điều này chỉ có thể dẫn đến việc thực thi JavaScript khi không có mã JavaScript khác thực thi:
setTimeout()
: tác nhân (ví dụ: trình duyệt) sẽ đặt một sự kiện vào hàng đợi sự kiện khi hết thời gian chờ. Việc theo dõi thời gian và việc đặt sự kiện trong hàng đợi xảy ra bởi mã không phải là JavaScript và do đó bạn có thể tưởng tượng điều này xảy ra song song với việc thực thi tiềm năng của một số mã JavaScript. Nhưng cuộc gọi lại được cung cấp setTimeout
chỉ có thể thực thi khi mã JavaScript hiện đang chạy đã hoàn tất và hàng đợi sự kiện thích hợp đang được đọc.
fetch()
: tác nhân sẽ sử dụng các chức năng của HĐH để thực hiện yêu cầu HTTP và theo dõi mọi phản hồi đến. Một lần nữa, tác vụ không phải JavaScript này có thể chạy song song với một số mã JavaScript vẫn đang thực thi. Nhưng thủ tục giải quyết lời hứa, sẽ giải quyết lời hứa được trả về fetch()
, chỉ có thể thực thi khi JavaScript hiện đang thực thi đã hoàn tất.
requestAnimationFrame()
: công cụ kết xuất của trình duyệt (không phải JavaScript) sẽ đặt một sự kiện vào hàng đợi JavaScript khi nó sẵn sàng thực hiện thao tác vẽ. Khi sự kiện JavaScript được xử lý, chức năng gọi lại được thực thi.
queueMicrotask()
: ngay lập tức đặt một sự kiện trong hàng đợi microtask. Cuộc gọi lại sẽ được thực hiện khi ngăn xếp cuộc gọi trống và sự kiện đó được tiêu thụ.
Có nhiều ví dụ khác, nhưng tất cả các chức năng này được cung cấp bởi môi trường máy chủ, không phải bởi lõi EcmaScript. Với EcmaScript lõi, bạn có thể đặt đồng bộ một sự kiện vào Hàng đợi công việc đầy hứa hẹn với Promise.resolve()
.
Ngôn ngữ xây dựng
ECMAScript cung cấp một số cấu trúc ngôn ngữ để hỗ trợ các mô hình không đồng pha, chẳng hạn như yield
, async
, await
. Nhưng đừng để nhầm lẫn: không có mã JavaScript nào bị gián đoạn bởi một sự kiện bên ngoài. Các "gián đoạn" đó yield
và await
dường như cung cấp chỉ là một kiểm soát, cách xác định trước trở về từ một cuộc gọi chức năng và khôi phục bối cảnh thực hiện của nó sau này, hoặc bằng cách mã JS (trong trường hợp yield
), hoặc hàng đợi sự kiện (trong trường hợp await
).
Xử lý sự kiện DOM
Khi mã JavaScript truy cập API DOM, trong một số trường hợp, điều này có thể khiến API DOM kích hoạt một hoặc nhiều thông báo đồng bộ. Và nếu mã của bạn có một trình xử lý sự kiện lắng nghe điều đó, nó sẽ được gọi.
Điều này có thể xuất hiện dưới dạng đồng thời có trước, nhưng không phải là: một khi (các) trình xử lý sự kiện của bạn trả về, API DOM cuối cùng cũng sẽ trả về và mã JavaScript gốc sẽ tiếp tục.
Trong các trường hợp khác, API DOM sẽ chỉ gửi một sự kiện trong hàng đợi sự kiện thích hợp và JavaScript sẽ nhận nó sau khi ngăn xếp cuộc gọi đã được xóa.
Xem các sự kiện đồng bộ và không đồng bộ