Đây là một loại câu hỏi thú vị để tìm hiểu sâu hơn.
Khi bạn làm điều này:
verifier(3,4).then(...)
trả về một lời hứa mới yêu cầu một chu trình khác quay lại vòng lặp sự kiện trước khi lời hứa mới bị từ chối đó có thể chạy .catch()trình xử lý sau đó. Chu kỳ bổ sung đó cho chuỗi tiếp theo:
verifier(5,4).then(...)
cơ hội để chạy .then()trình xử lý của nó trước dòng trước đó .catch()vì nó đã ở trong hàng đợi trước khi .catch()trình xử lý từ dòng đầu tiên có trong hàng đợi và các mục được chạy từ hàng đợi theo thứ tự FIFO.
Lưu ý rằng nếu bạn sử dụng .then(f1, f2)biểu mẫu thay cho biểu mẫu .then().catch(), nó sẽ chạy khi bạn mong đợi vì không có lời hứa bổ sung và do đó không có thêm đánh dấu nào liên quan:
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
Lưu ý, tôi cũng gắn nhãn tất cả các tin nhắn để bạn có thể xem verifier()chúng đến từ cuộc gọi nào, giúp bạn dễ dàng đọc kết quả hơn rất nhiều.
Thông số kỹ thuật của ES6 về thứ tự gọi lại lời hứa và giải thích chi tiết hơn
Thông số ES6 cho chúng ta biết rằng "công việc" của lời hứa (khi nó gọi một cuộc gọi lại từ a .then()hoặc .catch()) được chạy theo thứ tự FIFO dựa trên thời điểm chúng được chèn vào hàng đợi công việc. Nó không đặt tên cụ thể là FIFO, nhưng nó chỉ định rằng các công việc mới được chèn vào cuối hàng đợi và các công việc được chạy từ đầu hàng đợi. Điều đó thực hiện đặt hàng FIFO.
PerformPromiseThen (thực thi lệnh gọi lại từ .then()) sẽ dẫn đến EnqueueJob , đó là cách trình xử lý giải quyết hoặc từ chối được lên lịch chạy thực sự. EnqueueJob chỉ định rằng công việc đang chờ xử lý được thêm vào phía sau hàng đợi công việc. Sau đó, hoạt động NextJob kéo mục từ phía trước hàng đợi. Điều này đảm bảo thứ tự FIFO trong việc phục vụ các công việc từ hàng đợi công việc Promise.
Vì vậy, trong ví dụ trong câu hỏi ban đầu, chúng ta nhận được các lệnh gọi lại cho verifier(3,4)lời hứa và verifier(5,4)lời hứa được chèn vào hàng đợi công việc theo thứ tự chúng được chạy vì cả hai lời hứa ban đầu đó đều được thực hiện. Sau đó, khi trình thông dịch quay trở lại vòng lặp sự kiện, đầu tiên nó sẽ nhận verifier(3,4)công việc. Lời hứa đó bị từ chối và không có cuộc gọi lại nào cho điều đó trong verifier(3,4).then(...). Vì vậy, những gì nó làm là từ chối lời hứa đã verifier(3,4).then(...)trả về và điều đó khiến verifier(3,4).then(...).catch(...)trình xử lý được chèn vào jobQueue.
Sau đó, nó quay trở lại vòng lặp sự kiện và công việc tiếp theo mà nó kéo từ jobQueue là verifier(5, 4)công việc. Điều đó có một lời hứa đã được giải quyết và một trình xử lý giải quyết nên nó gọi trình xử lý đó. Điều này làm cho response (5,4):đầu ra được hiển thị.
Sau đó, nó quay trở lại vòng lặp sự kiện và công việc tiếp theo mà nó kéo từ jobQueue là verifier(3,4).then(...).catch(...)công việc mà nó chạy điều đó và điều này khiến error (3,4)đầu ra được hiển thị.
Đó là bởi vì .catch()chuỗi thứ nhất nằm sâu hơn một mức hứa hẹn trong chuỗi của nó so với .then()chuỗi thứ hai gây ra thứ tự mà bạn đã báo cáo. Và, đó là bởi vì chuỗi lời hứa được chuyển từ cấp độ này sang cấp độ tiếp theo thông qua hàng đợi công việc theo thứ tự FIFO, không đồng bộ.
Khuyến nghị chung về việc dựa vào chi tiết lập lịch biểu này
FYI, nói chung, tôi cố gắng viết mã không phụ thuộc vào mức kiến thức thời gian chi tiết này. Mặc dù nó gây tò mò và đôi khi hữu ích để hiểu, nhưng nó là mã mỏng manh vì một thay đổi đơn giản tưởng như vô hại đối với mã có thể dẫn đến sự thay đổi về thời gian tương đối. Vì vậy, nếu thời gian là quan trọng giữa hai chuỗi như thế này, thì tôi thà viết mã theo cách buộc thời gian theo cách tôi muốn hơn là dựa vào mức độ hiểu biết chi tiết này.