100行以内的代码实现一个Promise

422 阅读5分钟

Promise实现的基石就是,先了解Promises/A+规范,我按照自己的理解简单描述下。

Promise规范

术语:

  • 解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
  • 拒绝(reject):指一个 promise 失败时进行的一系列操作。
  • 终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
  • 拒因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。

要求

Promise 的状态

必须为下列三种:等待态(Pending)执行态(Fulfilled)拒绝态(Rejected)。 Pending可变为Fulfilled、Rejected,Fulfilled和Rejected是最终的状态,不能再改变,分别拥有对应的终值或拒因。

Then方法

promise.then(onFulfilled:function, onRejected?:function):Promise

需要执行onFulfilled或onRejected,如果不是函数,直接传给下一个Promise执行。

返回:

执行onFulfilled或onRejected有返回值x:

  1. x存在,直接执行[[Resolve]](promise2, x)
  2. x不存在,直接执行[[Resolve]](promise2, undefined)
  3. 报错error,直接执行[[Reject]](promise2, error)(规范里没说错误处理,和Promise解决过程应该是差不多的)

Promise解决过程([[Resolve]](promise, x))

如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。

x 与 promise 相等:

如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

x 为 Promise,promise 接受 x 的状态 :

  1. 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
  2. 如果 x 处于执行态,用相同的值执行 promise
  3. 如果 x 处于拒绝态,用相同的据因拒绝 promise

x 为对象或函数

规范里x只要有其暴露出一个遵循 Promise/A+ 协议的 then 方法,就会当作Promise处理,如果抛出异常就拒绝,这我没有实现。

如果 x 不是含有then,以 x 为参数执行 promise

实现

我的Promise数据结构如下

class AjPromise {
  private state: PENDING|FULFILLED|REJECTED = PENDING;
  private _value: any;   //终值
  private _reason: any; //拒因
  private _next: Array<AjPromise> = []; //下一链的Promise
  private _ok: Function;   //保存成功回调函数
  private _ng: Function;   //保存失败回调函数
  
  public then(onFulfilled?: Function, onRejected?: Function) {...}
  public catch(onRejected?: Function) {...}
  
  private _fire(value,state){...}
 
}

我是利用类似于链表的结构实现Promise链的

Promise里中ok用来保存成功回调函数,ng用来保存失败回调函数

then函数是用来创建一个新的Promise,并给它添加ng和ok函数

catch函数也是用来创建一个新的Promise,并给它添加ng函数

这两函数创建的新Promise都要加到Promise链上去。

_next是一个数组,用来保存下一链的Promise

还有一个私有函数_fire,是用来执行回调方法,修改自身状态,并把执行结果通知给下一链(next)里的Promise门

举个例子

const promise1 = new AjPromise((resolve, reject) =>{
    setTimeout(()=>{
      resolve(1)
    },100)
});

const promise2 = promise1.then(()=>{
    console.log("promise2");
    
    return new AjPromise((resolve, reject) =>{
        setTimeout(()=>{
          resolve(1)
        },100)
    })
},()=>{})

const promise4 = promise2.then((v)=>{
    console.log("promise4",v);
})

const promise3 = promise1.then((v)=>{
    throw new Error("我错了");
})

const promise5 = promise3.catch((v)=>{
    console.log(v);
})

构建结束后大概就是下图

构建好Promise链的执行过程大致如下:

  1. Promise1执行resolve(1), 把结果(终值, 状态)传给下一链的Promise2,和Promise3;
  2. Promise2是先注册的,先执行,这里状态是Fulfilled,执行成功回调函数,ok返回了一个Promise6,就直接把Promise2._next赋给返回的Promise6,由它通知下一链更新;
  3. Promise3,执行成功回调函数,抛出错误error,把结果(拒因, 状态)传给下一链的Promise5;
  4. Promise5执行失败回调函数;
  5. Promise6成功执行, 把结果(终值, 状态)传给下一链的Promise4,通知它更新。

代码

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

function isFunc(f) {
  return typeof f === 'function';
}

class AjPromise {
  state = PENDING;
  _value;
  _reason;
  _next = [];
  _ok;
  _ng;

  constructor(fn) {
    if(isFunc(fn)){
      const resolve = (value)=>this._fire(value,FULFILLED);
      const reject  = (value)=>this._fire(value,REJECTED);
      try {
        fn(resolve, reject);
      } catch (e) {
        reject(e);
      }
    }
  }
  then(onFulfilled, onRejected) {
    let promise = new AjPromise();
    promise._ok = onFulfilled;
    promise._ng = onRejected;
    this._next.push(promise);
    // 如果上一个Promise已经完成,直接触发这一个Promise执行回调函数
    try {
        if(this.state === REJECTED){
            setTimeout(()=>{promise._fire(this._reason ,REJECTED)},0);
        }else if(this.state === FULFILLED){
            setTimeout(()=>{promise._fire(this._value ,FULFILLED)},0);
        }
    } catch (error) {
        setTimeout(()=>{promise._fire(error ,REJECTED)},0);
    }
    return promise;
  }
  catch(onRejected) {
    let promise = new AjPromise();
    promise._ng = onRejected;
    this._next.push(promise);
    return promise;
  }
  //触发执行Promise函数,并修改状态
  _fire(value,state){
    if(state === FULFILLED){
      this._value||(this._value = value);
      this.state = PromiseHandle(this,this._ok,this._value,state);
    }else if(state === REJECTED){
      this._reason||(this._reason = value);
      this.state = PromiseHandle(this,this._ng,this._reason,REJECTED);
    }
  }
}
//Promise回调处理函数
function PromiseHandle (promise,handle,value,state){
  let res;
  if(value instanceof AjPromise){
    value._fire(null,value.state);
    return value.state;
  }
  if(isFunc(handle)){//是函数,执行
    try {
      if(promise.state === PENDING){
        res = handle(value);
      }else{ //执行过就不执行
        res = this._value;
      }
    } catch (error) {
      for (const p of promise._next) {//通知下一链失败回调
        if(p instanceof AjPromise && p.state === PENDING)
          setTimeout(()=>{p._fire(error,REJECTED)},0);
      }
      return REJECTED;
    }
    //对返回结果处理
    if(res instanceof AjPromise){
      res._next = promise._next;
    }else{
      for (const p of promise._next) {//通知下一链成功回调
        if(p instanceof AjPromise && p.state === PENDING)
          setTimeout(()=>{p._fire(res,FULFILLED)},0);
      }
    }
  }else {//不是函数,直接传给下一链
    for (const p of promise._next ) {
      if(p instanceof AjPromise && p.state === PENDING){
        setTimeout(()=>{p._fire(value,state)},0);
      }
    }
  }
  return FULFILLED;
};

测试代码: 这是Promise 链式调用顺序引发的思考文章里的例子,看上面的实现代码,就可以了解它的输出结果是啥了,我就不贴结果了,大家可以想一下,再对比结果。

new AjPromise((resolve, reject) => {
    console.log("log: 外部promise");
    resolve();
  })
    .then(() => {
      console.log("log: 外部第一个then");
      new AjPromise((resolve, reject) => {
        console.log("log: 内部promise");
        resolve();
      })
        .then(() => {
          console.log("log: 内部第一个then");
        })
        .then(() => {
          console.log("log: 内部第二个then");
        });
    })
    .then(() => {
      console.log("log: 外部第二个then");
    });