从0手把手实现一个promise

216 阅读5分钟

本文将基于 Promise A+ 规范从零实现一个完整的 PromisePromise A+链接

序言

Promise 是异步编程的一种解决方案,相比传统的解决方案——回调函数和事件——它更合理和强大。

一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)执行态(Fulfilled)拒绝态(Rejected),并且状态变化是不可逆的。

41.png 用法:

let promise = new Promise((resolve,reject) => {
  resolve('success')
})
promise.then(data => { // 成功的回调
  console.log(data)
}, err => { // 失败的回调
  console.log(err)
})

特点:

  1. 实例化 Promise 会传入一个 executor 执行器,该函数会立即执行。
new Promise(() => {})
  1. 执行 executor 时会传入两个函数,执行 resolve 会把当前 promise 的状态置为Fulfilled,执行 reject 会把 promise 的状态置为 Rejected,并且状态是不可逆的(不能先成功在失败、先失败在成功等)。
new Promise((resolve, reject) => {
  resolve('success')
  reject('err') // 不会生效
})
  1. 返回的实例上会有 then 方法,执行 then 方法时会传入两个回调函数。如果当前 promise 的状态为 Fulfilled,会执行第一个回调函数,函数参数即为调用 resolve 函数时传入的参数。反之,promise 状态为 Rejected, 执行第二个回调函数, 函数参数即为调用 reject 函数时传入的参数。
const promise = new Promise((resolve) => {
  resolve('success')
})

promise.then(data => { // data即为调用resolve函数时传入的参数
  console.log(data) // 打印 'success'
}, (err) => {
  console.log(err)
})
------------------------------------------------------------------------------
const promise2 = new Promise((resolve, reject) => {
  reject('error')
})

promise2.then(data => { 
  console.log(data) 
}, (err) => { // err即为调用reject函数时传入的参数
  console.log(err) // 打印 'error'
})
  1. 返回了成功的 promise 或一个普通值(包括 undefined),走下一个 then 的成功,返回了失败的 promise 或抛出错误,走下一个 promise 的失败。
const promise = new Promise((resolve, reject) => {
  resolve('success')
})

promise.then(data => { 
  return 'success'
  // return new Promise(resolve => resolve('success'))
}).then(data => { 
  console.log(data) // 打印 'success'
})
------------------------------------------------------------------------------
const promise = new Promise((resolve, reject) => {
  reject('error')
  // throw new Error('错误了')
})

promise.then(data => { 
  console.log(data)
}, (err) => {
  console.log(err) // 打印 'error'
  // 由于没有返回值,相当于 return undefined
}).then(data => { 
  console.log(data) // 打印 'undefined'
})
  1. Promise.resolvePromise.reject 相比,前者有等待效果。如果 Promise.resolve 的值还是 promise,它会等待这个 promise 执行完成。 如下实例:
const promise = Promise.resolve(new Promise((resolve) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
}))

promise.then(data => {
  console.log(data)
})
  1. 有的时候我们会在异步中调用 resolvereject 函数,此时由于状态一直是 Pending, 当调用 then 函数时,既不会调用成功的回调,也不会调用失败的回调。因此我们可以先把用户传入的回调函数收集起来,当调用 resolvereject 函数时,让收集的回调函数依次触发。
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success') // 当用户调用resolve 函数时,拿到成功回调数组中的每一项依次执行
  }, 3000)
})

promise.then(data => { // 先把(data) => {} 函数放到成功回调的数组中, 把(err) => {} 函数放到失败回调的数组中。
  console.log(data)
}, (err) => {
  console.log(err)
})
  1. 实例化 Promise 函数后,会返回该对象的实例。该实例上会有 thencatchfinally 等一些列方法。 Promise 上的静态方法有 allrace
Promise.all = function(promises) {} // 并发执行(通过计数的方式来实现)
Promise.race = function(promises) {} // 竞赛

let p = new Promise((resolve) => resolve('success'))
p.then(() => {}, () => {})
p.catch(() => {})
p.then().finally(() => {})

核心流程

const PENDING = 'PENDING' // 等待态
const RESOLVED = 'RESOLVED' // 成功态
const REJECTED = 'REJECTED' // 失败态

function resolvePromise (promise2, x, resolve, reject){
    // 1) 不能引用同一个对象 可能会造成死循环
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
    }
    let called // 确保不会既调用resolve,又调用reject 
    if ((typeof x ==='object' && x!=null) || typeof x === 'function') {
      try {
        let then = x.then
        if (typeof then === 'function') { // 认为是promise
          // call 改变this指向 并且让函数执行
          then.call(x, y => { // y的值可能还是promise,递归解析直至普通值
            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)
    }
}

class Promise {
  constructor(executor) {
    this.status = PENDING // 默认是等待态
    this.value = undefined // 存储resolve时用户传入的值
    this.reason = undefined // 失败reject时用户传入的值
    this.onResolvedCallbacks = [] // 存放成功的callback
    this.onRejectedCallbacks = [] // 存放失败的callback

    let resolve = (value) => {
      if (value instanceof Promise) {
        value.then(resolve, reject) // resolve的值还是promise,递归解析直至普通值
        return
      }
      if (this.status === PENDING) { // 保证状态不可逆
        this.value = value
        this.status = RESOLVED
        this.onResolvedCallbacks.forEach(fn => fn()) // 发布订阅
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    } 
    try {
      executor(resolve, reject) // 执行executor 传入resolve和reject
    } catch (e) {
      reject(e) // 如果内部出错直接将e手动的调用reject方法向外传递
    }
  }
  
  catch(errCallback) { // catch就是没有成功方法的then函数
    return this.then(null, errCallback)
  }
  
  then(onfulfilled, onrejected) { // then的参数可选
    onfulfilled = typeof onfulfilled == 'function'? onfulfilled : v => v
    onrejected = typeof onrejected == 'function' ? onrejected: err => { throw err }
    // 为了实现链式调用 必须创建一个新的promise并返回(因为状态不可逆)
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) { // 执行成功的回调
        setTimeout(() => {
          try {
            let x = onfulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      
      if (this.status === REJECTED) { // 执行失败的回调
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2,x,resolve,reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      
      if (this.status === PENDING) {  // executor有异步逻辑, 比如在setTimeout中调用resolve或reject
        this.onResolvedCallbacks.push(() => { // AOP 切片编程
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            } 
          }, 0)
        })
      }
    })

    return promise2 // 返回新创建的promise
  }
    
  static reject(err) { // 快速创建一个失败的promise
    return new Promise((resolve, reject) => {
      reject(err)
    })
  }   
}

Promise.resolve

特点: 快速创建一个成功的 promise。接收 promise 做参数时,会等到其执行完成。

Promise.resolve = function (data) {
  return new Promise((resolve, reject) => {
    resolve(data)
  })
}

Promise.reject

特点: 快速创建一个失败的 promise,不会有等待效果。

Promise.reject = function (err) {
  return new Promise((resolve, reject) => {
    reject(err)
  })
}

Promise.all

特点: 都成功才算成功, 有一个失败即为失败。

function isDef (v: any): boolean {
  return v !== undefined && v !== null
}

function isPromise (val: any): boolean { // vue2中的判断标准
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let arr = []
    let idx = 0
    
    let processData = (value, index) => {
      arr[index] = value
      if(++idx === promises.length){
        resolve(arr)
      }
    }
    
    for(let i = 0 ; i < promises.length; i++){
      let currentValue  = promises[i]
      if (isPromise(currentValue)){
        currentValue.then(data => {
          processData(data, i)
        }, reject)
      } else {
        processData(currentValue,i);
      }
    }
  })
}

Promise.race

特点: 有一个成功即为成功, 有一个失败即为失败。

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for(let i = 0 ; i < promises.length; i++){
      let currentValue  = promises[i]
      if (isPromise(currentValue)){
        currentValue.then(data => {
          resolve(data)
        }, reject)
      } else {
        resolve(currentValue)
      }
    }
  })
}