Trình xử lý hứa hẹn .then/ .catch/ .finallyluôn không đồng bộ.

Ngay cả khi Lời hứa được giải quyết ngay lập tức, code trên các dòng bên dưới .then / .catch/ .finallyvẫn sẽ thực thi trước các trình xử lý này.

Đây là bản demo:

let promise = Promise.resolve();

promise.then(() => alert("promise done!"));

alert("code finished"); // this alert shows first

Nếu bạn chạy nó, bạn sẽ thấy code finishedđầu tiên, và sau đó promise done!.

Điều đó thật lạ, bởi vì lời hứa chắc chắn được thực hiện ngay từ đầu.

Tại sao .thenkích hoạt sau đó? Chuyện gì đang xảy ra vậy?

1.Hàng đợi Microtasks

Nhiệm vụ không đồng bộ cần quản lý thích hợp. Do đó, tiêu chuẩn ECMA chỉ định một hàng đợi bên trong PromiseJobs, thường được gọi là hàng đợi microtask của LINE (thuật ngữ ES8).

Như đã nêu trong đặc t:

  • Hàng đợi là vào trước xuất trước: các nhiệm vụ được xử lý trước được chạy trước.
  • Việc thực thi một nhiệm vụ chỉ được bắt đầu khi không có gì khác đang chạy.

Hay nói một cách đơn giản hơn, khi một lời hứa đã sẵn sàng, những .then/catch/finallyngười xử lý nó được đưa vào hàng đợi; họ chưa được thực hiện Khi công cụ JavaScript trở nên miễn phí từ code hiện tại, nó sẽ nhận một tác vụ từ hàng đợi và thực thi nó.

Đó là lý do tại sao code đã hoàn thành trong một ví dụ ở trên.

Những người xử lý hứa hẹn luôn đi qua hàng đợi nội bộ này.

Nếu có một chuỗi có nhiều chuỗi .then/catch/finally, thì mỗi chuỗi được thực thi không đồng bộ. Đó là, đầu tiên nó được xếp hàng, sau đó được thực thi khi code hiện tại hoàn thành và các trình xử lý xếp hàng trước đó đã kết thúc.

Điều gì nếu thứ tự quan trọng đối với chúng ta? Làm thế nào chúng ta có thể thực hiện code finishedchạy sau promise done?

Dễ dàng, chỉ cần đặt nó vào hàng đợi với .then:

Promise.resolve()
  .then(() => alert("promise done!"))
  .then(() => alert("code finished"));

Bây giờ thứ tự là như dự định.

2. Từ chối không được giải quyết

Ghi nhớ unhandledrejectionsự kiện từ bài viết Xử lý lỗi với lời hứa?

Bây giờ chúng ta có thể thấy chính xác làm thế nào JavaScript phát hiện ra rằng có một sự từ chối chưa được xử lý.

Một sự từ chối không được xử lý khác đã xảy ra khi một lỗi lời hứa không được xử lý ở cuối hàng đợi microtask.

Thông thường, nếu chúng ta gặp lỗi, chúng ta sẽ thêm .catchvào chuỗi lời hứa để xử lý:


let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));

// doesn't run: error handled
window.addEventListener('unhandledrejection', event => alert(event.reason));

Nhưng nếu chúng ta quên thêm .catch, sau đó, sau khi hàng đợi microtask trống, động cơ sẽ kích hoạt sự kiện:

let promise = Promise.reject(new Error("Promise Failed!"));

// Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

Điều gì xảy ra nếu chúng ta xử lý lỗi sau này? Như thế này:


let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')), 1000);

// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

Bây giờ, nếu chúng ta chạy nó, chúng ta sẽ thấy Promise Failed!đầu tiên và sau đó caught.

Nếu chúng ta không biết về hàng đợi microt Nhiệm vụ, chúng ta có thể tự hỏi: Tại sao trình xử lý unhandledrejection chạy? Chúng tôi đã bắt và xử lý lỗi!

Nhưng bây giờ chúng ta hiểu rằng nó unhandledrejectionđược tạo ra khi hàng đợi microtask hoàn tất: công cụ kiểm tra các lời hứa và, nếu bất kỳ trong số chúng ở trạng thái bị từ chối, thì kích hoạt sự kiện.

Trong ví dụ trên, .catchđược thêm bởi setTimeoutcũng kích hoạt. Nhưng nó làm như vậy sau đó, sau khi unhandledrejectionđã xảy ra, vì vậy nó không thay đổi bất cứ điều gì.

3. Tóm lược

Việc xử lý bằng lời hứa luôn không đồng bộ, vì tất cả các hành động hứa hẹn đều đi qua các công việc hứa hẹn nội bộ Hàng đợi, còn được gọi là hàng microtask hàng đợi (thuật ngữ ES8).

Vì vậy, .then/catch/finallytrình xử lý luôn được gọi sau khi code hiện tại kết thúc.

Nếu chúng ta cần đảm bảo rằng một đoạn mã được thực thi sau đó .then/catch/finally, chúng ta có thể thêm nó vào một .thencuộc gọi được xâu chuỗi .

Trong hầu hết các công cụ Javascript, bao gồm các trình duyệt và Node.js, khái niệm microt Nhiệm vụ được liên kết chặt chẽ với vòng lặp sự kiện và Vì những điều này không liên quan trực tiếp đến các lời hứa, chúng được đề cập trong một phần khác của hướng dẫn.

Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!