根据Promise/A+规范实现Promise

192 阅读4分钟

根据Promise/A+规范实现Promise

Promise/A+规范实现Promise,promises-aplus-tests测试通过。

const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`));
  }
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    let then, called;
    try {
      then = x.then;
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, r => {
          if (called) return;
          called = true
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) return;
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}
const isPromise = (value) => {
  if ((typeof value === 'object' && value !== null) || typeof value === 'function') {
    return typeof value.then === 'function'
  }
  return false
}
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledCallBack = []
    this.onRejectedCallBack = []
    const resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
        this.onFulfilledCallBack.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
        this.onRejectedCallBack.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => { return value; }
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err; }
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === PENDING) {
        this.onFulfilledCallBack.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.onRejectedCallBack.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
  catch(errCallBack) {
    return this.then(null, errCallBack)
  }
  finally(callback) {
    return this.then(value => {
      return Promise.resolve(callback()).then(() => value)
    }, err => {
      return Promise.resolve(callback()).then(() => { throw err; })
    })
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let arr = [], i = 0;
      let processData = (index, data) => {
        arr[i] = data
        if (++i === promises.length) {
          resolve(arr)
        }
      }
      if (promises instanceof Array) {
        promises.forEach((p, index) => {
          if (isPromise(p)) {
            p.then((data) => {
              processData(index, data)
            }, reject)
          } else {
            processData(index, p)
          }
        })
      } else {
        reject()
      }
    })
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      if (promises instanceof Array) {
        for (let i = 0; i < promises.length; i++) {
          let current = promises[i]
          if (isPromise(current)) {
            if (current.value || (!isPromise(current.value) && current.status !== PENDING)) {
              resolve(current.value)
            }
            current.then(resolve, reject)
          } else {
            resolve(current)
          }
        }
      } else {
        reject()
      }
    })
  }
  static try(callback) {
    return new Promise((resolve, reject) => {
      return Promise.resolve(callback()).then(resolve)
    })
  }
}
Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd;
}
module.exports = Promise
/**
 * 根据Promise/A+规范
 * 实现Promise步骤解析:
 * 1.Promise的构造函数接收一个函数
 *  executor(resolve, reject)
 *   接收2个参数
 *      resolve 成功后执行函数,改变value和status
 *      reject  失败后执行函数,改变reason和status
 * 注意:
 *    1.一旦创建它就会立即执行,中途无法取消
 *    2.一旦状态改变就不会在变
 *    3.如果resolve接收的是一个promise,就让它执行then方法并且采用它的状态
 *    4.如果抛出异常,需要处理异常情况
 * 2.Promise实例生成后,用then方法指定fulfilled 和 rejected状态的回调
 *    1).同步函数
 *        直接判断status的状态,分别调用onFulfilled或onRjected
 *    2).异步函数
 *        需要用到订阅发布模式
 *        定义两个数组分别保存fulfilled 和 rejected的回调函数,
 *          再异步执行到resolve或reject的时候,
 *          改变状态获取value或reason,
 *          再去执行数组中保存的回调函数
 * 3.promise的链式调用
 *    特点:
 *      1).then中返回一个新的promise对象
 *        promise2 = new Promise(executor);
          return promise2;
 *      2).链式调用中,下一个then的状态采用它上一个then的返回
 *          需要保存第一个then返回的值,
 *          a.如果第一个then返回的是普通值(除function,object),会走下一个then的成功
 *          b.如果第一个then抛出错误,会走下一个then的失败
 *          c.如果第一个then返回的是一个promise,就让它执行并且采用它的状态
 *              此过程需要递归,直到再没有promise为止
 * 注意:
 *    1.onFulfilled 和 onRejected 不能在当前上下文执行
 *      解决办法:使用setTimeout
 *        使用setTimeout的还有一个目的:保证能拿到promise2
 *    2.then中2个回调都不存在时的处理
 *      解决办法:不存在时,需要自己提供默认的函数
 * 4.catch的实现
 *     catch的原理:成功回调函数为null时的then方法
 *     then(null,onRejected)
 * 5.all
 *    原理:
 *      1).返回一个新的Promise实例(所有的都成功才成功,有一个失败就失败)
 *      2).接收一个数组,数组中都是Promise实例,
 *          如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
 *      3).只有数组中所有的promise的状态都变成fulfilled,状态才会变成fulfilled,返回值组成一个数组,传递给外层的回调函数。
        4).只要数组中有一个是rejected,状态就变成rejected,此时第一个被reject的实例的返回值,会传递给外层的回调函数。
 * 6.finally
    原理:
      1).无论如何都执行
      如果回调函数中返回promise,等待promise执行完毕,再走下一个then/catch(捕获finally上一个then的成功和失败)
 * 7.race
 *    原理:
 *      1).返回一个新的Promise实例
 *      2).接收一个数组,数组中都是Promise实例,
 *          如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
 *      3).promise数组中谁快返回谁的结果 * 
 * 8.Promise.resolve和Promise.reject的实现
 *    Promise.resolve原理:返回一个成功的Promise
 *    Promise.reject原理:返回一个失败的Promise
 * 9.Promise.try
 *    原理:
 *      1).不管是同步还是异步错误都能捕获
 */