There are 5 static methods in the Promise class. We’ll quickly cover their use cases here.

Promise.resolve

The syntax:

let promise = Promise.resolve(value);

Returns a resolved promise with the given value.

Same as:

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

The method is used when we already have a value, but would like to have it “wrapped” into a promise.

For instance, the loadCached function below fetches the url and remembers the result, so that future calls on the same URL return it immediately:

function loadCached(url) {
  let cache = loadCached.cache || (loadCached.cache = new Map());

  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;
    });
}

We can use loadCached(url).then(…), because the function is guaranteed to return a promise. That’s the purpose Promise.resolve serves in the line (*): it makes sure the interface is unified. We can always use .then after loadCached.

Promise.reject

The syntax:

let promise = Promise.reject(error);

Create a rejected promise with the error.

Same as:

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

We cover it here for completeness, rarely used in real code.

Promise.all

Let’s say we want to run many promises to execute in parallel, and wait till all of them are ready.

For instance, download several URLs in parallel and process the content when all are done.

That’s what Promise.all is for.

The syntax is:

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

It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise.

The new promise resolves when all listed promises are settled and has an array of their results.

For instance, the Promise.all below settles after 3 seconds, and then its result is an array [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

Please note that the relative order is the same. Even though the first promise takes the longest time to resolve, it is still first in the array of results.

A common trick is to map an array of job data into an array of promises, and then wrap that into Promise.all.

For instance, if we have an array of URLs, we can fetch them all like this:

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}`)
  ));

A bigger example with fetching user information for an array of github users by their names (or we could fetch an array of goods by their ids, the logic is same):

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 ready, we can show HTTP status codes
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // shows 200 for every url
    }

    return responses;
  })
  // map array of responses into 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)));

If any of the promises is rejected, Promise.all immediately rejects with that error.

For instance:

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!

Here the second promise rejects in two seconds. That leads to immediate rejection of Promise.all, so .catch executes: the rejection error becomes the outcome of the whole Promise.all.

In case of an error, other promises are ignored

If one promise rejects, Promise.all immediately rejects, completely forgetting about the other ones in the list. Their results are ignored.

For example, if there are multiple fetch calls, like in the example above, and one fails, other ones will still continue to execute, but Promise.all don’t watch them any more. They will probably settle, but the result will be ignored.

Promise.all does nothing to cancel them, as there’s no concept of “cancellation” in promises. In another chapter we’ll cover AbortController that aims to help with that, but it’s not a part of the Promise API.

Promise.all(...) allows non-promise items in iterable

Normally, Promise.all(...) accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it’s wrapped in Promise.resolve.

For instance, here the results are [1, 2, 3]:

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

So we are able to pass non-promise values to Promise.all where convenient.

Promise.allSettled

Yeni eklendi
Bu dile yeni eklenen bir özelliktir. Eski tarayıcılar pollyfill gerektirebilir

Promise.all rejects as a whole if any promise rejects. That’s good in cases, when we need all results to go on:

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

Promise.allSettled waits for all promises to settle: even if one rejects, it waits for the others. The resulting array has:

  • {status:"fulfilled", value:result} for successful responses,
  • {status:"rejected", reason:error} for errors.

For example, we’d like to fetch the information about multiple users. Even if one request fails, we’re interested in the others.

Let’s use 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}`);
      }
    });
  });

The results in the line (*) above will be:

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

So, for each promise we get its status and value/reason.

Polyfill

If the browser doesn’t support Promise.allSettled, it’s easy to polyfill:

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

In this code, promises.map takes input values, turns into promises (just in case a non-promise was passed) with p => Promise.resolve(p), and then adds .then handler to it.

That handler turns a successful result v into {state:'fulfilled', value:v}, and an error r into {state:'rejected', reason:r}. That’s exactly the format of Promise.allSettled.

Then we can use Promise.allSettled to get the results or all given promises, even if some of them reject.

Promise.race

Similar to Promise.all, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it.

The syntax is:

let promise = Promise.race(iterable);

For instance, here the result will be 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

So, the first result/error becomes the result of the whole Promise.race. After the first settled promise “wins the race”, all further results/errors are ignored.

Summary

There are 5 static methods of Promise class:

  1. Promise.resolve(value) – makes a resolved promise with the given value.
  2. Promise.reject(error) – makes a rejected promise with the given error.
  3. Promise.all(promises) – waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of Promise.all, and all other results are ignored.
  4. Promise.allSettled(promises) (a new method) – waits for all promises to resolve or reject and returns an array of their results as object with:
    • state: 'fulfilled' or 'rejected'
    • value (if fulfilled) or reason (if rejected).
  5. Promise.race(promises) – waits for the first promise to settle, and its result/error becomes the outcome.

Of these five, Promise.all/allSettled are the most common in practice.

Eğitim haritası

Yorumlar

yorum yapmadan önce lütfen okuyun...
  • Eğer geliştirme ile alakalı bir öneriniz var ise yorum yerine github konusu gönderiniz.
  • Eğer makalede bir yeri anlamadıysanız lütfen belirtiniz.
  • Koda birkaç satır eklemek için <code> kullanınız, birkaç satır eklemek için ise <pre> kullanın. Eğer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)