关于Promise的多种方法具体实现

133 阅读5分钟

关于Promise的多种方法具体实现

1. Promise.resolve()

/**
 * Promise.resolve是在promise上的构造方法,根据传递进来的参数可以转为新的对象
 * 1) 参数是一个promise实例,不做任何修改,直接返回实例
 * 2) 参数是一个thenable对象,会将这个对象修改为promise,并且执行then方法,返回状态为fulfilled,值为then的值。
 * 3) 参数不是thenable对象或者不是对象,返回一个promise,状态fulfilled,值为传进来的值
 * 4) 没有参数,直接返回状态为fulfilled的promise。
 * 
 *  */ 
Promise.resolve = function(value) {
  // 1)
  if (value instanceof Promise) {
    return value;
  }
  return new Promise((resolve, reject) => {
    try {
      let then = value && value.then;
      if (typeof then === 'function') {
        // 2)
        then.call(value, (v) => {
          resolve(v);
        }, (r) => {
          reject(r);
        })
        // 这里也可以简写 value.then(resolve,reject), 在promise实现中需要上面这么写的原因是要判断used,而在这里并不需要,因此可以简写。
      } else {
        // 3) 4) 如果value是空,则resolve(undefined)完全正确。
        resolve(value);
      }
    } catch(e) {
      // 在取then或者执行then方法报错,则捕捉出来,报错信息就是返回的promise的reason,同时返回promise的状态是reject。
      reject(e);
    }
    
  })
  
}

关于Promise.resolve,注解写的非常详细了,值得注意的一点是,但是在大多数人的实现中,并没有捕捉catch

2. Promise.reject()

/**
 * Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected,值为reject传入的值。
 */
Promise.reject = function(reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  })
}

reject的实现非常简单,注意:你哪怕reason传入的是一个thenable对象,也会reject这个对象,因此相比较于Promise.resolve,Promise.reject的实现非常简单。

3. Promise.prototye.finally()

/**
 * finally方法是一个特殊方法,只要当promise的状态确定下来,无论是fulfilled还是rejected都会执行的操作。
 * 接受的参数是一个函数,只是执行这个函数,执行完毕后返回一个promise,这个promise的状态和值都是与之前传入的一致。
 */
Promise.prototype.finally = function(onfinally) {
  // finally必须得是promise确定了状态,因此直接用then即可实现
  return this.then(
    // 当状态是fulfilled时,返回一个promise,这个promise返回了value,状态也是fulfilled。 
    (value) => { return Promise.resolve(onfinally).then(() => value) },
    // 逻辑同上
    (reason) => { return Promise.resolve(onfinally).then(null, () => reason) }, 
  )
}

注意:onfinally这个参数是一个回调函数,是一个已经执行了的函数,而不是一个函数名。

4. Promise.all()

/**
 * Promise.all接受一个数组,返回的是一个promise,promise的状态和值分为2种情况
 * 1) 数组里的promise全变成fulfilled, 返回的promise状态是fulfilled, 数组里的promise的值组成为数组作为返回promise的值。
 * 2) 数组里的promise有一个rejected, 返回的promise状态是rejected, 第一个rejected的原因作为返回promise的值。
 */
Promise.all = function(promises) {
  return new Promise((resolve,reject) => {
    const len = promises.length;
    // 将异步操作的结果都储存下来。
    const result = [];
    // 传入空数组,直接确定状态和值。
    if (len === 0) return resolve(result);
    // 计数器,当所有promises都结束即可确定返回的promise状态和值
    let count = 0;
    // 遍历promises数组
    for (let i = 0; i < len; i++) {
      // 当其中某一个promises确定状态后 ps: 必须得用Promise.resolve(),有可能传入的不是promise
      Promise.resolve(promises[i]).then((data) => {
        result[i] = data;
        // 1) 当所有promises都已经确定状态
        if (++count === len) {
          resolve(result);
        }
      }, (reason) => {
        // 2)
        reject(reason);
      })
    }
  })
}

Promise.all的实现是比较复杂的,用一个计数器每次确定一个promise的状态就累加,当计数器等于传进来的数组长度后,就可以直接确定返回的promise状态了。

理清逻辑后,需要注意的点就是,传入的数组里可能会有不是promise的值,需要转成Promise.resolve确保它一定是个promise之后在此基础上进行操作。

请务必搞明白Promise.all()的实现,之后的所有方法都在Promise.all()理解后能够迅速掌握。

5. Promise.race()

/**
 * Promise.race与Promise.all类似,传参与返回值一致,只是返回的promise状态判断与Promise.all不一样。
 * 返回promise的状态根据传入的promises最先改变状态的一致。
 */
Promise.race = function(promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then((value) => {
        resolve(value);
      }, (reason) => {
        reject(reason)
      })
    }
  })
}

相比较于Promise.allPromise.race的实现基本没有任何难度。

6. Promise.allSettled

/**
 * Promise.allSettled与Promise.all很像,参数返回值一致。唯一的区别就是遇见reject时会把rejected的原因放入返回的promise的值 数组中。
 * Promise.allSettled返回的promise状态一定是fulfilled,
 * 返回的值是具有固定格式的对象{status: 'fulfilled', value} or {status: 'rejected‘, reason}
 */
Promise.allSettled = function(promises) {
  return new Promise((resolve, reject) => {
    const len = promises.length;
    let count = 0;
    const result = [];
    if (len === 0) return resolve(result);
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then((value) => {
        result[i] = {'status': 'fulfilled', value};
      }, (reason) => {
        result[i] = {'status': 'rejected', reason};
      })
      // 既然无论状态是fulfilled or rejected都要执行下方的函数,不如直接用finally,这就是finally的意义
      .finally(() => {
        if (++count === len) {
          resolve(result);
        }
      }) 
    }
  })
}

与all也是类似的,只要将Promise.all吃透,这个也是没什么难度的,唯一需要注意的就是返回的数组里是一个对象,而不是像Promise.all一样返回的仅仅是值,原因很简单,就是为了区分每个promise的状态究竟是fulfilled还是rejected

7. Promise.any()

/**
 * Promise.any与Promise.race有一点类似,传入的promises,有一个成功,就直接返回成功的值,全部失败才会返回promise。
 * 1) promises里有一个成功,返回的promise的值和状态都与这个成功的一致。
 * 2) promises里没有一个是成功的,直接抛出一个异常,是一个 AggregateError错误。
 */
Promise.any = function(promises) {
  return new Promise((resolve,reject) => {
    const len = promises.length;
    let count = 0;
    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then(value => {
        // 1)
        resolve(value);
      }, (reason) => {
        if (++count === len) {
          // 2)
          reject(`AggregateError: All promises were rejected`);
        }
      })
    }
  })
}

这是一个ES2021才添加的方法,在reject的时候模拟了一下AggregateError

总结

Promise的各个方法,实现起来并不算太困难,主要的难点在于对于promise的then方法是否真正的理解了,对于promise的基础状态更改是否理解了。

在理解了then方法随着promise状态改变调用不同的方法后,再真的搞明白Promise.all的实现后,promise也就不会再难到你了。

关于promise方法的具体应用与介绍,可以参考阮一峰老师的博客。

es6.ruanyifeng.com/#docs/promi…