一篇文章搞定「Promise」

327 阅读4分钟

一、Promise规范

Promise是用于处理异步场景的规范,能有效的避免产生回调地狱的问题,使异步代码看起来更加简洁、清晰、统一 且易于维护。

Promise可以将一个异步任务包装成一个Promise对象。

有哪些是异步任务❓

最常见的例如远程请求、延时弹窗等

Promise规范认为每个异步任务对象都应该有两个阶段和三种状态。

两个阶段

  • 未决阶段 - unsettled 表示还未决定当前Promise的对象状态
  • 已决阶段 - settled 表示已经决定当前Promise的对象状态

三种状态

  • 挂起状态(未决阶段) - pending
  • 完成状态(已决阶段) - fulfilled
  • 失败状态(已决阶段) - rejected

他们之间存在以下逻辑:

  • 任务只能从未决阶段变为已决阶段,无法逆行
  • 任务只能从挂起状态变为完成状态或失败状态,无法逆行
  • 任务一旦完成或者失败,状态就固定下来了,无法改变

例如:

pending -> fulfilled ✅

pending -> rejected ✅

fulfilled -> pending ❎

状态改变

pending挂起到fulfilled成功完成,称为resolve

pending挂起到rejected失败完成,称为rejected

后续状态

Promise可以针对任务结果进行后续处理,处理成功后的状态称为onFulfilled,处理失败后的状态称为onRejected

二、实现Promise规范

基于以上的Promise规范,ES6给我们提供了一套API:

const p = new Promise((resolve, reject) => {
    // 将promise状态改为成功
    resolve('success')
})

const p = new Promise((resolve, reject) => {
    // 将promise状态改为失败
    reject('error')
})

resolve(data)携带的数据为需要传递的相关数据,例如上面的代码resolve('success')将会把"success"这个字符串携带到then(data => {})的第一个函数回调中;

reject(err)携带的数据为失败的原因,会把这个失败的原因携带到then(data => {}, err => {})的第二个回调函数中;

p.then(
  // 针对fulfilled状态的回调
  (data) => {
    console.log(data);
  },
  // 针对rejected状态的回调
  (errMsg) => {
    console.log(errMsg);
  }
);

三、关于链式调用

对于Promise失败的处理,除了.then(null, err => {})的第二个函数回调,还可以使用链式调用.catch()来处理,代码如下

p.then(
  // 针对fulfilled状态的回调
  (data) => {
    console.log(data);
  }
).catch(
  // 针对rejected状态的回调
  (errMsg) => {
    console.log(errMsg);
  }
);

.then()方法同时也会返回一个新的Promise对象,也可以针对这个返回的Promise对象进行后续处理。

关于用.then()之后返回的新Promise的状态

  • 若没有相关的后续任务,新的Promise的状态和前任务一致,数据为前任务返回的数据

    const p = new Promise((resolve, reject) => {
      reject("error");
    });
    
    const p2 = p.then((data) => {});
    

    以上代码,此时p2的状态为Promise {<rejected>: 'error'},由于返回的第二个Promise对象没有对第一个Promise对象做错误处理,所以新的Promise对象状态和第一个Promise对象状态一致。

    const p = new Promise((resolve, reject) => {
      resolve("success");
    });
    
    const p2 = p.catch((err) => {
      console.log(err);
    });
    

    以上代码,此时p2的状态为Promise {<fulfilled>: 'success'},由于返回的第二个Promise对象没有对第一个Promise对象做成功处理,所以新的Promise对象的状态和第一个Promise一致,且数据为第一个Promise对象返回的数据。

  • 若有后续任务未执行,新任务为挂起状态

    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("success");
      }, 2000);
    });
    
    const p2 = p.then((data) => {
      console.log(data);
    });
    

    以上代码,第一个Promise对象需要再2秒之后才能确定状态,所以在这2秒内,p2的Promise对象状态为Promise {<pending>},2秒之后状态会转变为Promise {<fulfilled>}

  • 若后续任务执行了,则根据执行有错无错,来判断新的Promise状态

    const p = new Promise((resolve, reject) => {
      resolve("success1");
    });
    
    const p2 = p.then((data) => {
      console.log(data);
      return "success2";
    });
    

    以上代码,针对第一个Promise对象进行了处理,并且返回了success2,那么此时p2的Promise对象状态为Promise {<fulfilled>: 'success2'}

    const p = new Promise((resolve, reject) => {
      resolve("success1");
    });
    
    const p2 = p.then((data) => {
      console.log(data);
      throw new Error("error");
    });
    

    以上代码,针对第一个Promise对象进行了处理,但处理过程中有错,那么此时p2的Promise对象状态为Promise {<rejected>}

  • 若后续任务返回了一个新的Promise任务对象,那么新的任务状态和数据与该返回的Promise任务对象一致

    const p = new Promise((resolve, reject) => {
      resolve("success1");
    });
    
    const p2 = p.then((data) => {
      return new Promise((resolve, reject) => {
        resolve("success2");
      });
    });
    

    以上代码,针对第一个Promise对象的后续处理,返回了一个新的Promise对象,那么此时p2的状态就与返回的这个新的Promise对象状态一致,新Promise对象成功p2就成功,新Promise对象失败p2也就失败。

四、Promise的静态方法

方法名作用
Promise.resolve(data)直接返回一个完成状态的任务
Promise.reject(reason)直接返回一个拒绝状态的任务
Promise.all([Promise1, Promise2])返回一个任务
任务数组全部成功则成功
任何一个失败则失败
Promise.any([Promise1, Promise2])返回一个任务
任务数组任一成功则成功
任务全部失败则失败
Promise.allSettled([Promise1, Promise2])返回一个任务
任务数组全部已决则成功
该任务不会失败
Promise.race([Promise1, Promise2])返回一个任务
任务数组任一已决则已决,状态和其一致

下一期复习手写Promise