手动实现promise

669 阅读7分钟

1、基础版promise的实现


// 1.promise是一个类
// 2.当使用promise的时候 会传入一个执行器,此执行器是立即执行
// 3.当前executor 给了两个函数可以来描述当前promise的状态。promise中有三个状态 成功态  失败态 等待态
// 默认为等待态  如果调用resolve会走到成功态,如果调用reject 或者发生异常 会走失败态
// 4.每个promise实例都有一个then方法
// 5.promise 一旦状态变化后不能更改
const PENDING = 'PENGING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
  constructor(executor) {
    this.status = PENDING 
    this.value = undefined // 成功结果
    this.reason = undefined // 失败原因
    const resolve = (value) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = FULFILLED
        this.value = value
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = REJECTED
        this.reason = reason
      }
    }
    try {
      executor(resolve,reject) // promise 有两个参数 分别为成功的执行函数 和失败的执行函数
    } catch (error) {
      reject(error) // 针对执行器立即执行的时候抛出错误 
    }
  }
  then(onFulfilled,onRejected) {
    if (this.status === FULFILLED){ // 执行成功回调
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) { // 执行失败回调
      onRejected(this.reason)
    }
  }
}

module.exports = Promise

2、promise的异步处理

针对场景:

let promise = new Promise((resolve,reject)=>{ // pending
  setTimeout(() => {
      resolve('ok')
  }, 1000);
})

// 当用户调用then方法的时候 此时promise可能为等待态, 先暂存起来,因为后续可能会调用resolve和reject, 等会再触发对应onFulfilled 或者 onRejected
promise.then((value)=>{ // then是异步的
    console.log('success1',value)
},(reason)=>{
    console.log('err',reason)
})

利用发布订阅将回调函数暂存 最后执行

const PENDING = 'PENGING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
  constructor(executor) {
    this.status = PENDING 
    this.value = undefined // 成功结果
    this.reason = undefined // 失败原因
    this.onResolvedCallbacks = [] // 存储成功的回调函数
    this.onRejectedCallbacks = [] // 存储失败的回调函数
    const resolve = (value) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = FULFILLED
        this.value = value
        // 发布
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = REJECTED
        this.reason = reason
        // 发布
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve,reject) // promise 有两个参数 分别为成功的执行函数 和失败的执行函数
    } catch (error) {
      reject(error) // 针对执行器立即执行的时候抛出错误 
    }
  }
  then(onFulfilled,onRejected) {
    // 订阅
    if (this.status === PENDING){ // 在pending状态的时候收集成功回调/失败回调 利用切片编程(AOP)实现成功/失败回调函数的可扩展性
      this.onResolvedCallbacks.push(()=> {
        // 此处可以增加自定义逻辑
        onFulfilled(this.value)
      })
      this.onRejectedCallbacks.push(()=> {
        // 此处可以增加自定义逻辑
        onRejected(this.reason)
      })
    }
    if (this.status === FULFILLED){ // 执行成功回调
      onFulfilled(this.value)
    }
    if (this.status === REJECTED) { // 执行失败回调
      onRejected(this.reason)
    }
  }
}

module.exports = Promise

3、promise的链式调用

当调用.then方法之后回返回一个新的promise

分析then方法的返回值

情况1: then中方法返回的是一个(普通值 不是promise)的情况, 会作为外层下一次then的成功结果

情况2: then中方法 执行出错 会走到外层下一次then的失败结果

清空3: 如果then中方法返回的是一个promise对象, 此时会根据promise的结果来处理是走成功还是失败 (传入的是成功或者失败的内容)

无论上一次then走是成功还是失败,只要返回的是普通值 都会执行下一次then的成功 如果返回一个失败的promise或者抛出异常,会走下一个then的失败

// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise (promise2,x,resolve,reject) {
  // 第一种情况 如果返回的x和promise2相同 直接抛异常
 if (promise2 === x) {
    return reject(new TypeError('错误'))
  }
  // 判断返回的是不是对象或者函数  只有返回的是对象或者函数才有可能是promise 
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    // 兼容其他的promise 避免重复调用出现异常
    let called = false
    // 通过.then方法判断是不是promise 由于.then的时候可能出现异常 
    try {
      let then = x.then
      // 如果then是一个函数 则认为是promise
      if (typeof then === 'function') {
        // 此处不建议直接使用x.then 这样写同样会触发getter可能会发生异常
        then.call(x,(y) => {
          if (called) return 
          called = true
          resolvePromise(promise2,x,resolve,reject) // 一直解析到不是promise为止
        }, (r) => {
          if (called) return 
          called = true
          reject(r)
        })
      } else {
        // then不是函数 直接调用resolve
        resolve(x)
      }
    } catch (error) {
      if (called) return 
      called = true
      reject(error)
    }
  } else {
    // 既不是对象也不是函数则为普通值 直接调用resolve
    resolve(x)
  }
}
const PENDING = 'PENGING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
  constructor(executor) {
    this.status = PENDING 
    this.value = undefined // 成功结果
    this.reason = undefined // 失败原因
    this.onResolvedCallbacks = [] // 存储成功的回调函数
    this.onRejectedCallbacks = [] // 存储失败的回调函数
    const resolve = (value) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = FULFILLED
        this.value = value
        // 发布
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) { // 只有在状态为pending的时候才能够修改状态
        this.status = REJECTED
        this.reason = reason
        // 发布
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve,reject) // promise 有两个参数 分别为成功的执行函数 和失败的执行函数
    } catch (error) {
      reject(error) // 针对执行器立即执行的时候抛出错误 
    }
  }
  // then参数是可选的
  then(onFulfilled,onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
    // 针对链式调用的处理 需要返回一个新的promise
    let promise2 = new Promise((resolve,reject) => {
      // 将一下代码包裹在new Promise中 是因为需要拿到then方法的返回值 即 x
        // 订阅
      if (this.status === PENDING){ // 在pending状态的时候收集成功回调/失败回调 利用切片编程(AOP)实现成功/失败回调函数的可扩展性
        this.onResolvedCallbacks.push(()=> {
          setTimeout(() => { // 由于需要拿到promise2的值作为后续then方法的判断 同步执行会造成死循环 
              // 此处可以增加自定义逻辑
          try { // 针对then方法中 抛出异常处理 throw new Error()
            let x = onFulfilled(this.value) // x为成功回调的返回值
            // 此x 可能是一个promise, 如果是promise需要看一下这个promise是成功还是失败 .then ,如果成功则把成功的结果 调用promise2的resolve传递进去,如果失败则同理
            // x的值是决定调用promise2的成功还是失败 如果是promise则取它的状态 如果是普通值 则直接调用resolve
            resolvePromise(promise2,x,resolve,reject)
          } catch (error) { // 如果then方法中抛出异常 直接走到下一个then的reject中
            reject(error)
          }
          }, 0);
        })
        this.onRejectedCallbacks.push(()=> {
          setTimeout(() => {
            // 此处可以增加自定义逻辑
            try { // 针对then方法中 抛出异常处理 throw new Error()
              let x = onRejected(this.reason) // x为失败回调的返回值
              resolvePromise(promise2,x,resolve,reject)
            } catch (error) { // 如果then方法中抛出异常 直接走到下一个then的reject中
              reject(error)
            }
            }, 0);
        })
      }
      if (this.status === FULFILLED){ // 执行成功回调
        setTimeout(() => {
          try { // 针对then方法中 抛出异常处理 throw new Error()
            let x = onFulfilled(this.value) // x为成功回调的返回值
            resolvePromise(promise2,x,resolve,reject)
          } catch (error) { // 如果then方法中抛出异常 直接走到下一个then的reject中
            reject(error)
          }
        }, 0);
      }
      if (this.status === REJECTED) { // 执行失败回调
        setTimeout(() => {
          try { // 针对then方法中 抛出异常处理 throw new Error()
            let x = onRejected(this.reason) // x为失败回调的返回值
            resolvePromise(promise2,x,resolve,reject)
          } catch (error) { // 如果then方法中抛出异常 直接走到下一个then的reject中
            reject(error)
          }
        }, 0);
      }
    })
   
    return promise2
    // 此处不能直接return this this是当前的promise 返回的必须是一个新的promise 如果是当前的promise promise的状态是一旦改变便无法再次更改的
  }
}

module.exports = Promise
测试promise

安装测试插件 npm install promises-aplus-tests -g

添加一下测试代码

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve= resolve;
        dfd.reject = reject;
    }); 
    return dfd;
}

命令行执行命令promises-aplus-tests xxx.js

4、promise的方法

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 result = []
      let times = 0
      const processSuccess = (index,val) => {
        result[index] = val
        if (++times === promises.length) {
          resolve(result)
        }
      }
      
      // 遍历所有传入的promise
      for(let i = 0; i < promises.length; i++) {
        let p = promises[i] // 获取传入的某一个参数
        // 判断传入的是promise还是普通值
        if (p && typeof p.then === 'function') {
          p.then((value) => {
            processSuccess(i,value) 
          }, reject)
        } else {
          // 如果是普通值 直接传入
          processSuccess(i,p)
        }
      }
    })
  }
  // race 方法如果其中一个完成了 其他的还是会执行的,并没有采用他的结果
   static race (promises) {
    return new Promise((resolve,reject) => {
    for(let i = 0; i < promises.length; i++) {
      let p = promises[i]
      if (p && typeof p.then === 'function') {
        p.then(resolve,reject) // 一旦成功就直接 停止
      } else {
        resolve(p)
      }
    }
  })
}
 catch(errorCallbackFn) {
    return this.then(null, errorCallbackFn)
  }
  finally (cb){
    return this.then((data) => {
        return Promise.resolve(cb()).then(n => {
          return data
        })
      },err=> {
        return Promise.resolve(cb()).then(n => { 
          throw err
        })
      })
  }
promisify和promisifyAll

promisify 主要的功能是将一个异步的方法转化成promise的形式 主要是给node来使用的

回调函数的参数 永远第一个是error

function promisify(fn) {
  return function (...args) {
    return new Promise((resolve,reject) => {
      fn(...args, (err,data) => {
        if (err) return reject(err)
        resolve(data)
      })
    })
  }
}
function promisifyAll(obj) {
  let o = {}
  for(let key in obj) {
    if (typeof obj[key] === 'function') {
      o[key + 'Promise'] = promisify(obj[key])
    }
  }
  return o
}