Có 5 phương thức tĩnh trong lớp Promise. Chúng ta sẽ nhanh chóng tìm hiểu các trường hợp sử dụng của họ ở đây.

1. Promise.all

Hãy nói rằng chúng ta muốn nhiều lời hứa thực hiện song song và đợi cho đến khi tất cả chúng sẵn sàng.

Ví dụ: tải xuống song song một số URL và xử lý nội dung sau khi hoàn tất.

Đó là những gì Promise.alldành cho.

Cú pháp là:

let promise = Promise.all([...promises...]);

Promise.all nhận một loạt các lời hứa (về mặt kỹ thuật có thể là bất kỳ lần lặp nào, nhưng thường là một mảng) và trả về một lời hứa mới.

Lời hứa mới giải quyết khi tất cả các lời hứa được liệt kê được giải quyết và mảng kết quả của chúng trở thành kết quả.

Chẳng hạn, phần Promise.allbên dưới lắng xuống sau 3 giây, và sau đó kết quả của nó là một mảng [1, 2, 3]:

Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member

Xin lưu ý rằng thứ tự của các thành viên mảng kết quả giống như trong các lời hứa nguồn của nó. Mặc dù lời hứa đầu tiên mất nhiều thời gian nhất để giải quyết, nhưng nó vẫn là lần đầu tiên trong mảng kết quả.

Một mẹo phổ biến là ánh xạ một mảng dữ liệu công việc thành một mảng các lời hứa và sau đó bọc nó vào Promise.all.

Ví dụ: nếu chúng ta có một loạt các URL, chúng ta có thể tìm nạp tất cả chúng như thế này:

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://api.github.com/users/jeresig'
];

// map every url to the promise of the fetch
let requests = urls.map(url => fetch(url));

// Promise.all waits until all jobs are resolved
Promise.all(requests)
  .then(responses => responses.forEach(
    response => alert(`${response.url}: ${response.status}`)
  ));

Một ví dụ lớn hơn với việc tìm nạp thông tin người dùng cho một mảng người dùng GitHub bằng tên của họ (chúng ta có thể tìm nạp một mảng hàng hóa bằng id của họ, logic giống hệt nhau):

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
  .then(responses => {
    // all responses are resolved successfully
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // shows 200 for every url
    }

    return responses;
  })
  // map array of responses into an array of response.json() to read their content
  .then(responses => Promise.all(responses.map(r => r.json())))
  // all JSON answers are parsed: "users" is the array of them
  .then(users => users.forEach(user => alert(user.name)));

Nếu bất kỳ lời hứa nào bị từ chối, lời hứa được trả lại bằng cách Promise.alltừ chối ngay lập tức với lỗi đó.

Ví dụ:

Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!

Ở đây lời hứa thứ hai từ chối trong hai giây. Điều đó dẫn đến một sự từ chối ngay lập tức Promise.all, vì vậy .catchthực thi: lỗi từ chối trở thành kết quả của toàn bộ Promise.all.

Trong trường hợp có lỗi, các lời hứa khác sẽ bị bỏ qua

Nếu một lời hứa từ chối, Promise.allngay lập tức từ chối, hoàn toàn quên đi những điều khác trong danh sách. Kết quả của họ bị bỏ qua.

Ví dụ: nếu có nhiều cuộc gọifetch, như trong ví dụ trên và một cuộc gọi thất bại, những cuộc gọi khác vẫn sẽ tiếp tục thực hiện, nhưng Promise.allsẽ không xem chúng nữa. Họ có thể sẽ giải quyết, nhưng kết quả của họ sẽ bị bỏ qua.

Promise.allkhông có gì để hủy bỏ chúng, vì không có khái niệm nào về việc hủy bỏ điều đó. Trong một chương khác, chúng ta sẽ đề cập AbortControllerđến điều đó có thể giúp với điều đó, nhưng đó không phải là một phần của lời hứa API.Promise.all(iterable) cho phép các giá trị thường xuyên không hứa hẹn iterable

Thông thường, Promise.all(...)chấp nhận một lần lặp (trong hầu hết các trường hợp là một mảng) của các lời hứa. Nhưng nếu bất kỳ đối tượng nào trong số đó không phải là một lời hứa, thì nó sẽ được chuyển đến mảng kết quả thế này.

Ví dụ, ở đây kết quả là [1, 2, 3]:

Promise.all([
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000)
  }),
  2,
  3
]).then(alert); // 1, 2, 3

Vì vậy, chúng ta có thể chuyển các giá trị sẵn sàng đến Promise.allnơi thuận tiện.

2. Promise.allSettled

Promise.alltừ chối toàn bộ nếu bất kỳ lời hứa nào từ chối. Điều đó tốt cho tất cả hoặc không có gì trong trường hợp sau, khi chúng ta cần tất cả các kết quả thành công để tiến hành:

Promise.all([
  fetch('/template.html'),
  fetch('/style.css'),
  fetch('/data.json')
]).then(render); // render method needs results of all fetches

Promise.allSettledchỉ chờ đợi tất cả các lời hứa để giải quyết, bất kể kết quả. Mảng kết quả có:

  • {status:"fulfilled", value:result} để trả lời thành công,
  • {status:"rejected", reason:error} cho các lỗi.

Ví dụ: chúng ta muốn lấy thông tin về nhiều người dùng. Ngay cả khi một yêu cầu thất bại, chúng tôi vẫn quan tâm đến những yêu cầu khác.

Hãy sử dụng Promise.allSettled:

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => { // (*)
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });

Các resultstrong dòng (*)trên sẽ là:

[
  {status: 'fulfilled', value: ...response...},
  {status: 'fulfilled', value: ...response...},
  {status: 'rejected', reason: ...error object...}
]

Vì vậy, đối với mỗi lời hứa, chúng ta nhận được trạng thái của nó và value/error.

2.1. Polyfill

Nếu trình duyệt không hỗ trợ Promise.allSettled, thật dễ dàng để polyfill:

if(!Promise.allSettled) {
  Promise.allSettled = function(promises) {
    return Promise.all(promises.map(p => Promise.resolve(p).then(value => ({
      state: 'fulfilled',
      value
    }), reason => ({
      state: 'rejected',
      reason
    }))));
  };
}

Trong code này, promises.maplấy các giá trị đầu vào, biến chúng thành các lời hứa (chỉ trong trường hợp một lời hứa không được thông qua) với p => Promise.resolve(p), và sau đó thêm .thentrình xử lý cho mọi người.

Trình xử lý đó biến một kết quả thành công valuethành {state:'fulfilled', value}và một lỗi reasonthành {state:'rejected', reason}. Đó chính xác là định dạng của Promise.allSettled.

Bây giờ chúng ta có thể sử dụng Promise.allSettledđể có được kết quả của tất cả các lời hứa đã cho, ngay cả khi một số trong số chúng từ chối.

3. Promise.race

Tương tự như vậy Promise.all, nhưng chỉ chờ đợi cho lời hứa đã được giải quyết đầu tiên và nhận được kết quả (hoặc lỗi).

Cú pháp là:

let promise = Promise.race(iterable);

Chẳng hạn, ở đây kết quả sẽ là 1:

Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

Lời hứa đầu tiên ở đây là nhanh nhất, vì vậy nó đã trở thành kết quả. Sau khi lời hứa đã được giải quyết đầu tiên, người thắng cuộc đua, tất cả các kết quả / lỗi tiếp theo sẽ bị bỏ qua.

4. Promise.resolve / reject

Các phương thức Promise.resolvePromise.rejecthiếm khi cần thiết trong code hiện đại, bởi vì async/awaitcú pháp khiến chúng hơi lỗi thời.

Chúng ta bảo vệ chúng ở đây cho đầy đủ và cho những người không thể sử dụng async/awaitvì một số lý do.

4.1. Promise.resolve

Promise.resolve(value)tạo ra một lời hứa được giải quyết với kết quả value.

Giống như:

let promise = new Promise(resolve => resolve(value));

Phương thức này được sử dụng để tương thích, khi một hàm được mong đợi sẽ trả lại một lời hứa.

Ví dụ: loadCachedhàm bên dưới tìm nạp một URL và ghi nhớ (lưu trữ) nội dung của nó. Đối với các cuộc gọi trong tương lai có cùng một URL, nó ngay lập tức nhận được nội dung trước đó từ bộ đệm, nhưng sử dụng Promise.resolveđể thực hiện lời hứa về nó, vì vậy giá trị được trả về luôn là một lời hứa:

let cache = new Map();

function loadCached(url) {
  if (cache.has(url)) {
    return Promise.resolve(cache.get(url)); // (*)
  }

  return fetch(url)
    .then(response => response.text())
    .then(text => {
      cache.set(url,text);
      return text;
    });
}

Chúng ta có thể viết loadCached(url).then(…), bởi vì chức năng được đảm bảo để trả lại một lời hứa. Chúng ta luôn có thể sử dụng .thensau loadCached. Đó là mục đích của Promise.resolvedòng (*).

4.2. Promise.reject

Promise.reject(error)tạo ra một lời hứa bị từ chối với error.

Giống như:

let promise = new Promise((resolve, reject) => reject(error));

Trong thực tế, phương pháp này gần như không bao giờ được sử dụng.

5. Tóm lược

Có 5 phương thức tĩnh của Promiselớp:

  1. Promise.all(promises)– chờ đợi tất cả các lời hứa sẽ giải quyết và trả về một mảng kết quả của chúng. Nếu bất kỳ lời hứa nào được từ chối, nó sẽ trở thành lỗi Promise.allvà tất cả các kết quả khác đều bị bỏ qua.
  2. Promise.allSettled(promises) (phương thức được thêm gần đây) – chờ đợi tất cả các lời hứa sẽ giải quyết và trả về kết quả của chúng dưới dạng một mảng các đối tượng với:
    • state: "fulfilled"hoặc"rejected"
    • value(nếu hoàn thành) hoặc reason(nếu bị từ chối).
  3. Promise.race(promises) – chờ đợi lời hứa đầu tiên được giải quyết và kết quả / lỗi của nó trở thành kết quả.
  4. Promise.resolve(value) – thực hiện một lời hứa được giải quyết với giá trị nhất định.
  5. Promise.reject(error) – thực hiện một lời hứa bị từ chối với lỗi đã cho.

Trong số năm, Promise.allcó lẽ là phổ biến nhất trong thực tế.

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!