学习笔记之Promise进阶:从A+规范到手动实现

本文详细介绍了Promise A+规范,包括Promise的状态转换、then方法的执行规则,以及如何手动实现一个Promise,涵盖了状态类型定义、构造函数、then和catch方法的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习笔记之Promise进阶:从A+规范到手动实现

一、Promise A+ 规范

(一)相关概念

  1. promise:一个具有then方法的对象/函数,其行为遵循 Promise A+ 规范;

  2. thenable:具有then方法的对象/函数;

  3. value:promise实例的状态为兑现/成功时的值,即 resolve 的参数,可为任意类型;

  4. reason:promise实例的状态为拒绝/失败时的值,即 reject 的值,表示拒绝/失败的原因;

  5. exception:异常值

(二)A+ 规范

  1. states

Promise实例的状态,共有三种: pending,fulfilled,rejected。

(1) pending

  • 初始状态,可以改变,在resolve / reject执行之前都是这个状态。

  • 在resolve执行后从pending状态改变为fufilled;

  • 在reject执行后从pending状态改变为rejected;

(2) fulfilled

  • 是一种最终状态,不可再发生改变;

  • 当处于pending状态的promise在经过resolve之后,其状态会变为fulfilled;

  • 必须有一个value值,一般为resolve传入的参数,若resolve没有传参,则value值为undefined;

(3)rejected

  • 也是一种最终状态,不可再发生改变;

  • 当处于pending状态的promis经过reject后,其状态会变为rejected;

  • 必须有一个reason值,一般为reject传入的参数,若未传参数,则reason值为undefined;

需要注意的是,promise的状态只能从pending状态转变为fulfilled或者rejected,不可逆转,也不会在fulfilled和rejected之间转变。因此,一旦promise的状态已经是fulfilled或者rejected,即使之后又经过了resolve或reject,promise的状态也不会再发生变化。

  1. then方法

根据 A+ 规范,promise应该提供一个then方法,接收两个参数,用于访问最终状态的结果。

const promise = new Promise((resolve,reject)=>{
  // ...
})
promise.then(onFulfilled, onRejected)
  • then方法的参数onFulfilled应该是一个function,如果不是,则 onFulfilled 应该被忽略,而使用内部默认的function来替代它。onRejected同理。

  • onFulfilled在promise的状态变为fulfilled后,应该调用onFulfilled,此时,onFulfilled的参数是value;而在promise状态变为fulfilled之前,则不应该调用此函数。此外,promise的状态只会发生一次变化,相对应的,一个onFulfilled也只能调用一次

  • onRejected在promise的状态变为rejected后,应该调用onRejected,此时,onRejected的参数是reason;而在promise状态变为rejected之前,则不应该调用此函数。此外,promise的状态只会发生一次变化,相对应的,一个onRejected也只能调用一次

  • onFulfilled 和 onRejected 的执行环境是在微任务里。可使用queueMicrotask( )来将其加入微任务队列,不使用setTimeout的原因是:setTimeout为宏任务,不符合 A+ 规范。

  • then方法可以多次调用。在 promise 的状态变为 fulfilled 后,其所有的 onFulfilled 回调按照then的顺序执行;在 promise 的状态变为rejected后,其所有的 onRejected 回调按照then的顺序执行;

    const promise = new Promise((resolve,reject)=>{
      // ...
    });
    // 可多次调用then方法,其中回调函数按照then的顺序
    promise.then(onFulfilled, onRejected);
    promise.then(onFulfilled, onRejected);
    promise.then(onFulfilled, onRejected);
    promise.then(onFulfilled, onRejected);
    
  • then方法的返回值

    then方法的返回值应该是一个船新的promise

    const promise1 = new Promise((resolve,reject)=>{
      // ...
    });
    // then方法返回一个新的promise,与
    const promise2 = promise.then(onFulfilled, onRejected);
    // promise2 同样可以调用then方法
    promise2.then(callback1, callback2)
    
    • 根据 onFilfilled 或者onRejected 执行的结果,假设为 a,调用resolvePromise( )来解析promise;

    • 当 onFulfilled 或 onRejected 执行时报错了,则 promise2 就需要被 reject ;

    • 若 promise1 的 then 方法中,onFulfilled 不是一个function,则会调用内部的默认函数,使 promise2 以 promise1 的 value 来触发 fulfilled 。

    • 若 promise1 的 then 方法中,onRejected 不是一个function,则会调用内部的默认函数,使 promise2 以 promise1 的 reason 来触发 rejected。

  • resolvePromise( )

    resolvePromise(promise2, a, resolve, reject)
    

    接下来进行条件判断:

    • promise2 === a,reject Type error;

    • 如果 a 是一个promise

      • a的状态是pending,则promise2也会处于pending状态直至 a 的状态改变;

      • a的状态是fulfilled,则promise2也以相同的value触发fulfilled;

      • a的状态是rejected,则promise2以相同的reason触发rejected;

    • 如果 a 是一个object或function

      尝试取 a 的then方法看是否出错:let then = a.then,若出错则把错误reject出去。取到then之后,判断then的类型,如果then是一个函数则通过call调用then:then.call(a),否则resolve(a);

    • 如果 a 是其他类型,则 resolve (a);

二、如何实现一个Promise

1.定义状态类型

const PENDING = 'pending',
  FULFILLED = 'fulfilled',
  REJECTED = 'rejected';

2.初始化class

定义初始状态value以及reason

class MyPromise {
  constructor() {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
  }
}

3.resolve和reject

  • 更改status,从pending变为fulfilled或rejected;

  • 更新value或reason值;

    class MyPromise {
      constructor() {
        // ... 初始化
      }
      resolve(value){
        if(this.status === PENDING){
          this.value = value;
          this.status = FULFILLED;
        }
      }
      reject(reason){
        if(this.status === PENDING){
          this.reason = reason;
          this.status = REJECTED;
        }
      }
    }
    

4.构造函数入参

new MyPromise((resolve, reject)=>{})
  • 入参为一个函数 resolver,resolver接收两个参数:resolve 和 reject;

  • 执行new Promise时,就会同步执行这个函数,发生任何的错误就会被reject出去。

class MyPromise {
  constructor(resolver) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    if(typeof resolver === 'function'){
      // resolver是函数则执行resolver
      // 错误捕获
      try {
        resolver(this.resolve.bind(this), this.reject.bind(this));
      } catch(e){
        this.reject(e)
      }
    }else{
      // resolver不是函数则抛出错误
      throw new Error(`Promise resolver ${resolver} is not a function`);
    }

  }
  resolve(value){
    // ...
  };
  reject(reason){
    // ...
  };
}

5.then方法

  • 入参:onFulfilledonRejected

  • 返回值:新的promise

  • 需要配判断入参是否为函数,如果不是则调用默认函数传递value和reason

先来看看下面这个写法。

class MyPromise {
  constructor(resolver){
    // ... 初始化、执行resolver
  }
  resolve(value){
    // ...
  }
  reject(reason){
    // ...
  }
  then(onFulfilled, onRejected){
    const realOnFulfilled = isFunction(onFulfilled) ? onFulfilled
      : (vaule) => value;

    const realOnRejected = isFunction(onRejected) ? onRejected
      : (reason) => {
          throw reason
        };
    const promise2 = new MyPromise((resolve, reject)=>{
      switch(this.status){
        case FULFILLED:
          realOnFulfilled();
          break;
        case REJECTED:
          realOnRejected();
          break;
      }
    });
    return promise2
  }
  isFunction(func){
    return typeof func === 'function'
  }
}

这个写法存在问题,只能处理同步操作,一旦有异步执行resolve或reject,则调用then方法时,status仍为pending。另外,then方法可以执行多次,因此,需要两个队列来存储realOnFulfilledrealOnrejected,一旦status状态为pending,则将realOnFulfilled和realOnRejected添加进队列里,以便后续status值发生变化时依次调用。因此做如下改进:

  • 增加两个队列FULFILLED_CALLBACK_LISTREJECTED_CALLBACK_LIST分别在pending状态时存放 realOnFulfilled 和 realOnRejected;

  • 在合适的时机调用队列里的回调函数,有两种方案:

    • 在resolve和reject里,当status变为fulfilled或rejected时调用相应队列里的函数;

    • 通过存取器 setter 来监听 status,一旦 status 发生变化,则一次调用相应队列里的处理程序。

    这里我选择了后者,能让代码结构更清晰。如果在resolve和reject里调用,会增加代码的复杂性和混乱程度。

  • 此外,根据 A+ 规范,当realOnFulfilled或realOnRejected为微任务环境,执行出错时,需要将错误reject出去,触发promise2的rejected。且根据其执行得到的结果 a 的不同,会在resolvePromise中有不同的操作。因此使用queueMicrotask( )将其放入微任务队列,并封装到fulfilledMicroTaskrejectedMicroTask中进行错误捕获。

class MyPromise {
  // 添加队列
  FULFILLED_CALLBACK_LIST = [];
  REJECTED_CALLBACK_LIST = [];

  // 因为使用了存取器,增加一个私有变量_status来储存真正的status的值
  _status = PENDING

  constructor(resolver){
    // ... 初始化、执行resolver
  }

  // 通过存取器setter来监听status
  // 需要一个私有变量_status来储存真正的status的值,否则在getter中会死循环
  set status(newStatus){
    this._status = newStatus;
    switch(newStatus){
      case FULFILLED:
        this.FULFILLED_CALLBACK_LIST.forEach((callback)=>callback(this.value))
        break;
      case REJECTED:
        this.REJECTED_CALLBACK_LIST.forEach((callback)=>callback(this.reason))
        break;
    }
  }
  get status(){
    return this._status
  }

  resolve(value){
    // ...
  }
  reject(reason){
    // ...
  }
  then(onFulfilled, onRejected){

    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled
      : (vaule) => value;

    const realOnRejected = this.isFunction(onRejected) ? onRejected
      : (reason) => {
          throw reason;
        };
    const promise2 = new MyPromise((resolve, reject)=>{
      // 捕获realOnFulfilled执行过程中的错误并reject出去
      const fulfilledMicroTask = () => {
        queueMicrotask(() => {
          try {
            const a = realOnFulfilled(this.value);
            this.resolvePromise(promise2, a, resolve, reject);
          } catch(e){
            reject(e);
          }
        })
      }
      // 捕获realOnRejected执行过程中的错误并reject出去
      const rejectedMicroTask = () => {
        queueMicrotask(() => {
          try {
            const a = realOnRejected(this.reason);
            this.resolvePromise(promise2, a, resolve, reject);
          } catch(e) {
            reject(e);
          }
        })
      }

      switch(this.status){
        // 成功时调用realOnFulfilled
        case FULFILLED:
          fulfilledMicroTask();
          break;
        // 拒绝时调用realOnRejected
        case REJECTED:
          rejectedMicroTask();
          break;
        // 等待时将其放入队列
        case PENDING:
          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicroTask);
          this.REJECTED_CALLBACK_LIST.push(rejectedMicroTask);
      }
    });
    return promise2;
  }

  resolvePromise(promise2, a, resolve, reject){
    // 根据a的值进行不同的操作
  }

  isFunction(func){
    return typeof func === 'function';
  }
}

接下来实现resolvePromise来解析 a 的结果。

// class MyPromise
// ...
resolvePromise(promise2, a, resolve, reject){
  if(promise2 === a){
    return reject(new TypeError('The promise and the return value are the same'));
  }
  if(a instanceof MyPromise){
    queueMicroTask(()=>{
      a.then(
        (res) => {
          this.resolvePromise(promise2, res, resolve, reject);
        },
        reject
      )
    })
  }else if(typeof a === 'object' || typeof a === 'function'){
    if(a === null){
      return resolve(a);
    }
    let then = null;
    try {
      then = a.then;
    } catch(e) {
      return reject(e)
    }
    if(this.isFunction(then)){
      // 保证函数只能调用一次
      let hasCalled = false;
      try {
        then.call(
          x, 
          (res) => {
            if(hasCalled) return;
            hasCalled = true;
            this.resolvePromise(promise2, res, resolve, reject)
          }, (e) => {
            if(hasCalled) return;
            hasCalled = true;
            reject(e)
          }
        )
      } catch(e) {
        if(hasCalled){
          return
        }
        reject(e)
      }
    }else{
      resolve(a)
    }
  }else{
    resolve(a)
  }
}

// ...

6. catch方法

  • catch实质上就是调用了then方法,给第一个参数传入null,返回一个新的promise,即promise2;

  • 在catch方法未执行完之前,promise2的status将一直是pending;

  • catch方法执行过程中如果报错,将触发promise2的status变为rejected;

  • 若catch方法执行完毕且没有报错,将触发promise2的status变为fulfilled;

  • 详见resolvePromise方法。

// class MyPromise
// ...

catch(onRejected){
  return this.then(null, onRejected)  
}

// ...

7. Promise.resolve()

静态方法,用提供的参数创建一个rosolve过的promise。

class MyPromise {
  // ...

  static resolve(value){
    // 只需要resolve,不需要reject
    return new MyPromise((resolve)=>{
      resolve(value)
    })
  }

  // ...
}

### 8. Promise.reject()

与Promise.resolve同理。

class MyPromise {
  // ...

  static reject(reason){
    return new MyPromise((resolve, reject)=>{
      reject(reason)
    })
  }

  // ...
}

至此,Promise的核心功能都已基本实现。还剩下Promise.all,Promise.race等静态方法,有空再研究。以上代码整合如下,如有不足或错误之处,还望不吝指出,感激不尽!

// 定义三种状态
const PENDING = 'pending',
  FULFILLED = 'fulfilled',
  REJECTED = 'rejected';

class MyPromise {
  // 添加队列
  FULFILLED_CALLBACK_LIST = [];
  REJECTED_CALLBACK_LIST = [];

  // 因为使用了存取器,增加一个私有变量_status来储存真正的status的值
  _status = PENDING

  constructor(resolver) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    if(typeof resolver === 'function'){
      // resolver是函数则执行resolver
      // 错误捕获
      try {
        resolver(this.resolve.bind(this), this.reject.bind(this));
      } catch(e){
        this.reject(e)
      }
    }else{
      // resolver不是函数则抛出错误
      throw new Error(`Promise resolver ${resolver} is not a function`);
    }

  }

  // 通过存取器setter来监听status
  // 需要一个私有变量_status来储存真正的status的值,否则在getter中会死循环
  set status(newStatus){
    this._status = newStatus;
    switch(newStatus){
      case FULFILLED:
        this.FULFILLED_CALLBACK_LIST.forEach((callback)=>callback(this.value))
        break;
      case REJECTED:
        this.REJECTED_CALLBACK_LIST.forEach((callback)=>callback(this.reason))
        break;
    }
  }
  get status(){
    return this._status
  }

  resolve(value){
    if(this.status === PENDING){
      this.value = value;
      this.status = FULFILLED;
    }
  }
  reject(reason){
    if(this.status === PENDING){
      this.reason = reason;
      this.status = REJECTED;
    }
  }

  then(onFulfilled, onRejected){

    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled
      : (vaule) => value;

    const realOnRejected = this.isFunction(onRejected) ? onRejected
      : (reason) => {
          throw reason;
        };
    const promise2 = new MyPromise((resolve, reject)=>{
      // 捕获realOnFulfilled执行过程中的错误并reject出去
      const fulfilledMicroTask = () => {
        queueMicrotask(() => {
          try {
            const a = realOnFulfilled(this.value);
            this.resolvePromise(promise2, a, resolve, reject);
          } catch(e){
            reject(e);
          }
        })
      }
      // 捕获realOnRejected执行过程中的错误并reject出去
      const rejectedMicroTask = () => {
        queueMicrotask(() => {
          try {
            const a = realOnRejected(this.reason);
            this.resolvePromise(promise2, a, resolve, reject);
          } catch(e) {
            reject(e);
          }
        })
      }

      switch(this.status){
        // 成功时调用realOnFulfilled
        case FULFILLED:
          fulfilledMicroTask();
          break;
        // 拒绝时调用realOnRejected
        case REJECTED:
          rejectedMicroTask();
          break;
        // 等待时将其放入队列
        case PENDING:
          this.FULFILLED_CALLBACK_LIST.unshift(fulfilledMicroTask);
          this.REJECTED_CALLBACK_LIST.unshift(rejectedMicroTask);
      }
    });
    return promise2;
  }

  catch(onRejected){
    return this.then(null, onRejected)  
  }

  resolvePromise(promise2, a, resolve, reject){
    if(promise2 === a){
      return reject(new TypeError('The promise and the return value are the same'));
    }
    if(a instanceof MyPromise){
      queueMicroTask(()=>{
        a.then(
          (res) => {
            this.resolvePromise(promise2, res, resolve, reject);
          },
          reject
        )
      })
    }else if(typeof a === 'object' || typeof a === 'function'){
      if(a === null){
        return resolve(a);
      }
      let then = null;
      try {
        then = a.then;
      } catch(e) {
        return reject(e)
      }
      if(this.isFunction(then)){
        // 保证函数只能调用一次
        let hasCalled = false;
        try {
          then.call(
            x, 
            (res) => {
              if(hasCalled) return;
              hasCalled = true;
              this.resolvePromise(promise2, res, resolve, reject)
            }, (e) => {
              if(hasCalled) return;
              hasCalled = true;
              reject(e)
            }
          )
        } catch(e) {
          if(hasCalled){
            return
          }
          reject(e)
        }
      }else{
        resolve(a)
      }
    }else{
      resolve(a)
    }
  }

  isFunction(func){
    return typeof func === 'function';
  }

  // 静态方法Promise.resolve()
  static resolve(value){
    // 只需要resolve,不需要reject
    return new MyPromise((resolve)=>{
      resolve(value)
    })
  }

  // 静态方法Promise.reject()
  static reject(reason){
    return new MyPromise((resolve, reject)=>{
      reject(reason)
    })
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值