Promise.then是如何实现链式调用的

前言

咱们都知道,then 方法返回一个新的 promise 实例,这是实现链式调用的根本。javascript

为了在 promise 状态发生变化时(resolve / reject 被调用时)再执行 then 里的函数,咱们使用一个 callbacks 数组先把传给then的函数暂存起来,等状态改变时再调用。java

那么,怎么保证后一个 then 里的方法在前一个 then(多是异步)结束以后再执行呢?数组

好比下面示例中,最后一个 then 应该在 2 秒后再打印,值为 2promise

var p1 = new MyPromise((resolve, reject) => {
      console.log('hhh')
      setTimeout(() => {
        resolve(1)
      }, 1000);
    }).then(res => {
      return new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(res+1)
        }, 1000);
      })
    }).then(res => {
      console.log(res);
      return res;
    })
复制代码

实现链式调用的核心

将传给 then 的函数和新 promiseresolve 一块儿 push 到前一个 promisecallbacks 数组中,达到承前启后的效果markdown

  • 承前:当前一个 promise 完成后,调用其 resolve 变动状态,在这个 resolve 里会依次调用 callbacks 里的回调,这样就执行了 then 里的方法了
  • 启后:上一步中,当 then 里的方法执行完成后,返回一个结果,若是这个结果是个简单的值,就直接调用新 promiseresolve,让其状态变动,这又会依次调用新 promisecallbacks 数组里的方法,循环往复。。若是返回的结果是个 promise,则须要等它完成以后再触发新 promiseresolve,因此能够在其结果的 then 里调用新 promiseresolve

then的代码实现:

then(onFulfilled, onReject){
    // 保存前一个promise的this
    const self = this; 
    return new MyPromise((resolve, reject) => {
      // 封装前一个promise成功时执行的函数
      let fulfilled = () => {
        try{
          const result = onFulfilled(self.value); // 承前
          return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
        }catch(err){
          reject(err)
        }
      }
      // 封装前一个promise失败时执行的函数
      let rejected = () => {
        try{
          const result = onReject(self.reason);
          return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
        }catch(err){
          reject(err)
        }
      }

      switch(self.status){
        case PENDING: 
          self.onFulfilledCallbacks.push(fulfilled);
          self.onRejectedCallbacks.push(rejected);
          break;
        case FULFILLED:
          fulfilled();
          break;
        case REJECT:
          rejected();
          break;
      }
    })
   }
复制代码

注意⚠️

  • 连续多个 then 里的回调方法是同步注册的,但注册到了不一样的 callbacks 数组中,由于每次 then 都返回新的 promise 实例(参考上面的例子和图)
  • 注册完成后开始执行构造函数中的异步事件,异步完成以后依次调用 callbacks 数组中提早注册的回调

那么,什么状况下一个 callbaks 数组中会有多个回调呢?异步

好比这种:
p1 完成时,注册在 p1callbacks 中的函数依次执行
如下代码 1 秒后打印出 1, 2 秒后打印出 2函数

var p1 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    }, 1000);
  })
var p2 =p1.then(res => {  //p1完成时的回调1
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(res+1)
    }, 1000);
  })
}).then(res => {
  console.log(res); // 2
  return res+1;
})

p1.then(res => {  // p1完成时的回调2
    console.log(res);  // 1
})
复制代码

可是,若是这么写:ui

var p1 = new MyPromise((resolve, reject) => {
      console.log('hhh')
      setTimeout(() => {
        resolve(1)
      }, 1000);
    }).then(res => {
      return new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(res+1)
        }, 1000);
      })
    }).then(res => {
      console.log(res); // 2
      return res+1;
    })

p1.then(res => {
    console.log(res);  // 3
})
复制代码

最后一个 p1.then 是在最后才打印出来的,由于 p1 的返回值是第三个 promise,最后一个 then 里的函数也是注册进了第三个 promisecallbacks 中。this

完整的 Promise 实现

// 定义三种状态
 const PENDING = 'PENDING';
 const FULFILLED = 'FULFILLED';
 const REJECT = 'REJECT';

 class MyPromise {
   constructor(fn){
     // 初始化状态
     this.status = PENDING;
     // 将成功、失败的结果放在this上,便于then、catch访问
     this.value = null;
     this.reason = null;

     // 成功态、失败态回调函数队列,同步调用then时将对应态的函数注册进去, 在状态变动的时候调用
     this.onFulfilledCallbacks = [];
     this.onRejectedCallbacks = [];

     const resolve = (value) => {
       if(this.status === PENDING){
         this.status = FULFILLED;
         this.value = value;
         // 成功态回调函数依次执行
         this.onFulfilledCallbacks.forEach(fn => fn(this.value))
       }
     }
     const reject = (reason) => {
      if(this.status === PENDING){
        this.status = REJECT;
        this.reason = reason;
        // 失败态回调函数依次执行
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
     }
     // 生成实例后当即调用fn
     // 把内部的resolve和reject传入fn,用户可调用resolve和reject
     try{
      fn(resolve, reject); 
     }catch(err){
       // fn执行出错,将错误内容用reject抛出去
       reject(err)
     }
     
   }
   then(onFulfilled, onReject){
    // 实现值穿透 当then中传入的不是函数,则这个promise返回上一个promise的值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onReject = typeof onReject === 'function' ? onReject : reason => { throw new Error(reason) }

    // 保存前一个promise的this
    const self = this; 
    return new MyPromise((resolve, reject) => {
      // 封装前一个promise成功时执行的函数
      let fulfilled = () => {
        try{
          const result = onFulfilled(self.value); // 承前
          return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
        }catch(err){
          reject(err)
        }
      }
      // 封装前一个promise失败时执行的函数
      let rejected = () => {
        try{
          const result = onReject(self.reason);
          return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
        }catch(err){
          reject(err)
        }
      }

      switch(self.status){
        case PENDING: 
          self.onFulfilledCallbacks.push(fulfilled);
          self.onRejectedCallbacks.push(rejected);
          break;
        case FULFILLED:
          fulfilled();
          break;
        case REJECT:
          rejected();
          break;
      }
    })
   }

   // Promise.prototype.catch就是Promise.prototype.then(null, onRejected)的别名
   catch(onRejected){
     return this.then(null, onRejected);
   }

  static resolve(value){
    // 若是是promise实例,直接返回
    if(value instanceof MyPromise){
      return value;
    }else{
      // 若是不是promise实例,返回一个新的promise对象,状态为fulfilled
      return new MyPromise((resolve, reject) => resolve(value))
    }
  }
  static reject(reason){
    // Promise.reject方法的参数会原封不动地做为reject的参数
      return new MyPromise((resolve, reject) => reject(reason))
  }

  /** * Promise.all() 接受一个数组,返回一个promise对象 * 全部的promise状态变为FULFILLED,返回的promise状态才变为FULFILLED。 * 一个promise状态变为REJECTED,返回的promise状态就变为REJECTED。 * 数组成员不必定都是promise,须要使用Promise.resolve()处理。 */
  static all(promiseArr){
    const len = promiseArr.length;
    const values = new Array(len);

    let count = 0; // 记录已经成功的promise个数
    return new MyPromise((resolve, reject) => {
      for(let i=0; i<len; i++){
        // Promise.resolve()处理,确保每个都是promise实例
        MyPromise.resolve(promiseArr[i]).then(
          val => {
            values[i] = val;
            count++;
            if(count === len) resolve(values); // 若是所有执行完,改变promise的状态为FulFilled
          },
          err => {
            reject(err)
          }
        )
      }
    })
  }
  static race(promiseArr){
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(item => {
        MyPromise,resolve(item).then(
          val => resolve(val),
          err => reject(err)
        )
      })
    })
  }
 }
复制代码