Advance JavaScript - Promise
Tags: javascript, webdev, beginners, programming
Published: 2024-01-23 08:19:30

This article primarily covers some common functions and usage techniques related to Promises, aiming to assist readers in utilizing Promises more easily to solve problems.
## Basic
### Promise
A Promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It is commonly used for handling asynchronous operations such as network requests, file operations, and database queries.

A Promise can be in one of three states:

Pending: The initial state. The Promise is neither fulfilled nor rejected.
1. Fulfilled: The Promise has been successfully resolved with a value. This state is also referred to as "resolved".
2. Rejected: The Promise encountered an error or failure during the asynchronous operation.

`new Promise()` returns a Promise object, this object can handle the result of the async.
```js
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* success */){
    resolve(value);
  } else {
    reject(error);
  }
});

// Ajax
const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
  });

  return promise;
};
```

### Promise.then(successFn, failFn);
When we need to handle the result of an asynchronous operation, we use the then method after a Promise object to process the result. The then method takes two parameters: the first one is the success callback function that receives the value resolved by resolve, and the second one is the error callback function that receives the value rejected by reject.

```js
getJSON("/posts.json").then(function(json) {
  console.log('result: ' + json);
}, function(error) {
  console.error(error);
});
```
The first successFn of the `then` function can return a value, which can then be accessed by the successFn of the next then function, forming a chain of calls.

```js
// You can use chaining to perform multiple asynchronous operations in sequence.
getJSON("/post/1.json").then(function(post) {
  setPost(post.article);
  return getJSON(post.commentURL);
}).then(function (comments) {
  setComments(comments);
}, function (err){
  console.log("rejected: ", err);
});
```

### Promise.catch(failFn);
As shorthand of the `then(null | undefined, failFn)`, You can place an error handler at the end to listen for errors from the previous then statements.

```js
const promise = new Promise((resolve, reject) => {
  resolve('resolve');
});

promise.then(res => {
  console.log(res);
  throw 'error msg';
}).catch(err => {
  console.log(err)
})

// "resolve"
// "error msg"
```

Once an error occurs, the execution of statements after the error will be stopped.

```js
const promise = new Promise((resolve, reject) => {
  resolve('resolve');
});

promise.then(res => {
  throw 'error msg';
  console.log(res);
}).catch(err => {
  console.log(err)
})

// "error msg"

const promise = new Promise((resolve, reject) => {
  reject('rejected');
});

promise.then(res => {
  throw 'error msg';
  console.log(res);
}).catch(err => {
  console.log(err)
})

// "rejected"
```
If a resolve operation is performed before a reject operation, the reject operation will not be executed. Conversely, if a reject operation is performed before a resolve operation, the resolve operation will not be executed. This follows the principle of Promises that once a state transition occurs, it cannot be changed again.

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba01de1cebd14cf78bb7b436dae62b76~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=636&h=448&s=49884&e=png&a=1&b=212225)

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38949f03184042cfba4bc0325ed4a903~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=626&h=449&s=49079&e=png&a=1&b=1b1c1f)

If an error occurs outside the current event loop, it cannot be caught.

```js
const promise = new Promise((resolve, reject) => {
  resolve('resolved')
  setTimeout(function () { throw 'error test' }, 0);
});

promise.then(res => {
  console.log(res);
}).catch(err => {
  console.log('err',err)
})
```
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ca37197131514e81843d3e76bd3b3654~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=963&h=152&s=24100&e=png&a=1&b=491619)

Because `catch` is a shorthand for `then`, you can chain `then` after the `catch`. However, if there is a catch method followed by a then method, any error that occurs before the subsequent catch method will not be caught.

```js
promise.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// carry on
```
### Promise.finally(onFinally);
The finally() method of a Promise instance is used to register a function that will be called when the promise is settled (either resolved or rejected). It immediately returns an equivalent Promise object, which allows you to chain other Promise methods. However, when chaining other methods, the finally() method will always return the previous result.

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b72492d6f174076bf308848aa3e2e57~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=784&h=473&s=36553&e=png&a=1&b=1b1c1f)

## Senior
### Promise.all(promiseArr)
The static method Promise.all() takes an iterable of Promises as input and returns a Promise. When all the input Promises are fulfilled, the returned Promise is also fulfilled (even if the iterable is empty) and resolves to an array containing all the fulfilled values. If any of the input Promises are rejected, the returned Promise is rejected with the reason of the first rejection.

```js
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// [3, 42, "foo"]

const promise1 = Promise.reject(3); // reject
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
}).catch(error => {
  console.log('error', error)
});
// "error" 3
// then does not react, catch will get the first reject value.
```
If we attach a catch function to each Promise instance, we can retrieve the corresponding catch value inside Promise.all(). If the catch function does not return any value, the corresponding item in the values array within the then method of Promise.all() will be undefined.

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2ad38cc273924c859c53737ccaf42d7b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=744&h=778&s=127804&e=png&a=1&b=1b1c1f)

### Promise.allSettled(promiseArr)
This function addresses the issue of `Promise.all` terminating when encountering a rejection. It will execute all the promises and then retrieve the results in the then method. It's important to note that the structure of the resolved object and the rejected object may differ slightly.

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/35004e9fc8f74289a62a73e7e1858882~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1054&h=567&s=76315&e=png&a=1&b=1b1c1f)

### Promise.any(promiseArr)

The static method `Promise.any()` takes an iterable of Promises as input and returns a Promise. When any of the input Promises is fulfilled, the returned Promise is fulfilled and returns the first fulfilled value. If all the input Promises are rejected (including when an empty iterable is passed), it will be rejected with an array of rejection reasons [AggregateError](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/97ad1f81d63e4176ac10dba23430a23a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=728&h=483&s=81569&e=png&a=1&b=1b1c1f)

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0825afa377a442c2b0381311924a4cb7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1057&h=490&s=101172&e=png&a=1&b=1b1c1f)

### Promise.race(promiseArr)
The static method Promise.race() takes an iterable of Promises as input and returns a Promise. The returned Promise will be settled (either fulfilled or rejected) as soon as the first Promise in the iterable is settled.

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba4754b9da4842e38a0d4a55163ca92c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=774&h=508&s=76016&e=png&a=1&b=1b1c1f)

[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)