[Javascript 进阶]-Promise

225 阅读5分钟

摘要

本文主要收录了关于Promise的一些常用函数和使用技巧,希望能够帮助大家更轻松的使用Promise解决问题。

基础操作

Promise

是解决异步的回调地狱的方案,可以用Promise将一个异步操作进行包裹,然后使用resolve和reject分别对满足和不满足的异步结果进行下一步触发,Promise对象有以下两个特点:

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

new Promise() 返回一个Promise的对象,这个对象可以进行对异步结果的操作。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    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);

当我们需要处理异步返回的结果时,就需要在Promise对象之后使用then对结果进行处理,then方法有两个参数,第一个是接受resolve返回的值,也就是成功的操作函数,第二个是接受reject返回的值,失败的操作函数。

getJSON("/posts.json").then(function(json) {
  console.log('结果: ' + json);
}, function(error) {
  console.error('出错了', error);
});

then函数的第一个successFn可以返回一个值,然后在下一个thensuccessFn可以拿到上一个返回的值,然后形成链式调用。

// 利用链式调用进行多次异步操作
// 获取文章
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);

作为 then(null | undefined, failFn)的缩写,可以放在末尾对之前的then进行错误监听

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"

一旦遇到报错,就不再执行报错之后的语句。

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"

如果在reject之前进行了resolve,则不会执行reject的操作,反之,在resolve之前进行了reject操作,则不会执行resolve,这遵循了Promise的一旦进行状态转变就不再转变的原则。 image.png

image.png 如果报错不在当前事件循环,则捕获不到报错

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

因为catch是then的一种缩写,所以catch后也可以链式调用then,但是在这之后的then方法中再报错之前的catch方法就捕获不到了。

promise.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// carry on

Promise.finally(onFinally);

Promise 实例的 finally() 方法用于注册一个在 promise 敲定(resolve或reject)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法,但是链式调用其他的方法,finally会固定返回上一个结果。

image.png

进阶操作

Promise.all(promiseArr)

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。

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 函数里面不会有任何回应, catch会捕获到第一个reject的值。

但是如果我们给每一个Promise实例都附上catch函数,则可以在all里面获取到对应catch的值,如果catch不返回任何值,则promise all的then中的values对应项为undefined。

image.png

Promise.allSettled(promiseArr)

这个函数解决了Promise.all遇到一个reject就结束的问题,它会执行完所有的promises,然后在then中得到结果,注意这里resolve的结果和reject结果的对象体有稍许不一样。 image.png

Promise.any(promiseArr)

Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。

image.png

image.png

Promise.race(promiseArr)

Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定

image.png

Promise.race() 与 Promise.any() 的对比

Promise.race() 是只要有一个Promise 敲定 (状态改变,resolve或者reject)就会返回,resolve则进入then,reject则进入catch。

Promise.any() 是只要有一个Promise 兑现 (resolve)就会返回,resolve之后则进入then,当所有promises都reject则进入catch。

参考文献

阮一峰es6入门 Promise

MDN