手写简单实现一下promise: then()、resolve()、reject()、all()、race()方法

1,336 阅读5分钟

一、首先要了解这几个关键问题:

1.如何改变Promise状态?

resolve(value);如果当前是pending就会变为resolved;
rejected(reason);如果当前是pending就会变为rejected;
抛出异常;如果当前是pending就会变为rejected;

2.一个promise指定多少个成功/失败的回调都会调用吗?

当promise改变对应的状态时会调用;

3.改变Promise状态和指定回调函数谁先谁后?

(1)都有可能,正常情况下是先指定回调再改变状态;但也可以先改变状态再指定回调;
(2)如何先该状态再指定回调?
在执行器中直接调用resolve()/rejected()延长更长的时间才调用then() (3) 什么时候才能得到数据?
先制定的回调,那当状态发生改变时,回调函数就会调用,得到数据;
如果先改变状态,那指定回调函数时,回调函数就会调用,得到数据;

4.Promise.then()返回的新promise()的状态由什么决定?

简洁来说:由then()指定的回调函数的结果来执行
详细来说: 1、如果抛出异常,新promise变为rejected 状态,err变为抛出的异常;
2、如果抛出非promise的任意值,新promise变为resolve状态, res为返回的值;包括(undefined\、null)
3、如果抛出新的promise, 此的promise返回的结果就是新promise的状态;

5.Promise如何串联多个操作任务?

(1)通过promise的then()返回一个新的Promise,可以看成then()的链式调用;
(2)通过then的链式调用串联都多个同步/异步任务

6.Promise异常穿透?

(1)当使用Promise.then()链式调用时,可以在最后指定失败的回调;
(2)前面任何操作出了异常,都会传到最后的失败的回调函数处理;

7.中断Promise链?

(1)使用Promise.then链式调用时,在中间中断,不再调用后面的回调函数;
(2)在回调函数中返回一个pending状态的Promise

二、了解这个几个问题之后我们再来手写实现思路会很清晰:

//匿名函数自调用表达式

(function (window) {
  //Promise 构造函数
  //excutor 执行器函数(同步)
  const PENDING = 'pending'
  const RESOLVED = 'resolved'
  const REJECTED = 'rejected'
  function Promise(excutor) {
    const _this = this
    _this.status = PENDING //给Promise指定status属性,用于存储Promise当前的状态,初始值为PENDING
    _this.data = undefined   //给Promise指定data属性,用于存储结果数据
    _this.callbacks = []     //给Promise指定callbacks属性,用于存储指定的回调函数{onRESOLVED () {}, onRejected () {}}
      
    function resolve (value) {
    console.log('this', this)
    //如果当前状态不是PENDING,直接返回
    if (_this.status !== PENDING) {
        return
    }

    //状态改为resolced
    _this.status = RESOLVED
    //保存value数据
    _this.data = value
    //如果有待执行的回调函数,立即异步调用指定的回调函数
    if (_this.callbacks.length > 0) {
        setTimeout(() => { //放入队列中执行待成功的回调
            _this.callbacks.forEach(callbackObj => {
                callbackObj.onResolved(value)
            });
            }
        })
    }

    function reject (reason) {
    //如果当前状态不是PENDING,直接返回
    if (_this.status !== PENDING) {
        return
    }
    //状态改为rejected
    _this.status = REJECTED
    //保存reason数据
    _this.data = reason
    //如果有待执行的回调函数,立即异步调用指定的回调函数
    if (_this.callbacks.length > 0) {
        setTimeout(() => { //放入队列中执行待成功的回调
            _this.callbacks.forEach(callbackObj => {
                callbackObj.onRejected(reason)
            });
            }
        })
    }

    try {
        excutor(resolve, reject)
    } catch (error) { //如果执行器捕获到异常,promise对象变为rejected状态
        reject(error)
    }
    
  }

  //then原型对象的方法
  //指定一个成功和者失败的回调,返回一个promise
  Promise.prototype.then = function (onResolved, onRejected) {
    //指定默认的回调
    onResolved === 'function' ? onResolved : value => value
    //错误穿透关键一步
    onResolved === 'function' ? onRejected : resaon => {throw reason}
    
    const _this = this
    // 调用指定的回调函数,根据回调函数指定的结果, 改变return的promise状态

    /**
     * 返回的promise的状态由onResolved/onRejected执行返回的promise的结果来决定:
     * 1.如果抛出异常,返回的promise为rejected状态;
     * 2.如果返回的是非promise的任意值,返回的promise为resolved状态;
     * 3.如果返回的是promise,return的promise的结果就是这个promise的结果;
     */
    const handel = function (callback) {
        try {
            const result = callback(_this.data)
            if (result instanceof Promise) {
                //3.如果回调函数返回的时promise,return的promise的结果就是这个promise的结果
                setTimeout(() => {
                    result.then(resolve, reject)
                })
            } else {
                //2.如果回调函数返回的不是promise,return的promise就会成功,value就是返回的值
                resolve(result)
            }
        } catch (error) {
            //1.如果抛出异常,return的promise就会失败,reason就是error
            reject(error)
        }
    }

    return new Promise((resolve, reject) => {
        //pending状态,将异步回调函数保存起来
        if (_this.status === PENDING) {
            _this.callbacks.push({
                onResolved (value) {
                    handel(onResolve)
                },
                onRejected (reason) {
                    handel(onReject)
                }
            })
        } else if (_this.status === RESOLVED) {
            //resolved状态,异步执行onResolved(){}并改变return的promise的状态
            handel(onResolved)
        } else {
            //rejected状态,异步执行onRejected(){}并改变return的promise的状态
            handel(onRejected)
        }
    })

  }

  //catch原型对象的方法
  Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
  }
  
  //resolve函数对象的方法
  //返回一个成功或失败的promise
  Promise.resolve = function (value) {
      //返回一个成功或失败的promise
    return new Promise((resolve, reject) => {
        //value是一个promise,使用value的结果作为promise的结果
        if (value instanceof Promise) {
            value.then(resolve, reject)
        } else {//value不是promise,返回一个成功的promise
            resolve(value)
        }
    })
  }

  //reject函数对象的方法
  //返回一个指定结果的失败的promise
  Promise.reject = function (reason) {
      // 返回一个失败的promise
    return new Promise((resolve, reject) => {
        reject(reason)
    })
  }

  //all函数对象的方法
  //返回一个promise,只有当所有的promise都成功时才返回成功,否则只要有一个失败就失败
  Promise.all = function (promises) {
      const values = new Array(promises.length)
      const promiseCount = 0
      return new Promise((resolve, rejevt) => {
          promises.forEach((promise, index) => {
              Promise.resolve(promise).then(
                  value => {
                      promiseCount++ //成功就+1
                      values[index] = value //将成功的promise放入指定的位置
                      if (promiseCount == promises.length) {
                          resolve(values) //都成功则成功
                      }
                  },
                  reason => {//只要有一个失败就失败
                      reject(reason)
                  }
              )
          })
      })
  }

  //race函数对象的方法
  //返回一个promise,根据第一个改变状态的结果来决定成功还是失败
  Promise.race = function (promises) {
      return new Promise((resolve, reject) => {
          Promise.resolve(promises).forEach(promise => {
              promise.then(
                  value => {
                      resolve(value)
                  },
                  reason => {
                      reject(reason)
                  }
              )
          })
      })
  }

  window.Promise = Promise
})(window)