Promise实现原理

174 阅读7分钟

Promise含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise基本结构

》》》 目标

const p = new Promise((resolve, reject) => {
   if ('异步操作成功'){
    resolve('成功了');
  } else {
    reject('失败了');
  }
})

Promise是一个构造函数,必须接收一个函数作为参数,我们称该函数为executor(执行器),executor又包含了两个函数作为参数,分别是resolve和reject,当异步操作成功后马上执行resolve,异步操作失败后,会马上执行reject

》》》代码实现

class MyPromise{
     constructor(executor){
          if(typeof executor !== 'function') {
              throw new Error('MyPromise必须接收一个函数作为参数')
          }
          // 执行传进来的函数executor
          try {
            executor(this.resolve.bind(this), this.reject.bind(this))
          } catch(err) {
            this.reject(err)
          }
      }
     resolve(){}
     reject(){}
}

Promise中有throw的话,就相当于执行了reject。这就要使用try catch

为什么要绑定this呢?这是为了resolve和reject的this指向永远指向当前的MyPromise实例,防止随着函数执行环境的改变而改变

Promise状态和值

状态

Promise有三种状态,分别是Pending(等待中),Fulfilled(已成功),Rejected(已失败)。

状态只能由Pending变成Fulfilled, 或由Pending变成Rejected,且状态确定后不会再发生变化(不能再次修改)。

Promise的值是指异步操作完成(状态改变)后传给回调函数的值。如上文,executor接收的两个函数resolve和reject就是用来改变状态和传递值的,resolve传递操作成功的值,reject传递操作失败的值

》》》代码实现

// 定义三种状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'reject'

class MyPromise{
  status = PENDING  // 设置初始状态
  value = undefined  // 设置初始值
  constructor(executor){
      if(typeof executor !== 'function') {
          throw new Error('MyPromise必须接收一个函数作为参数')
      }
      // 执行executor
      try {
        executor(this.resolve.bind(this), this.reject.bind(this))
      } catch(err) {
        this.reject(err)
      }
  }

  resolve(val){
    if (this.status !== PENDING) return
    this.status = FULFILLED  // 修改状态
    this.value = val  // 修改值
  }
  reject(err){
    if (this.status !== PENDING) return
    this.status = REJECT   // 修改状态
    this.value = err  // 修改值
  }
}

Promise核心:then方法

  • Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数

  • Promise 实例化时传入的函数会立即执行,then(...) 中的回调需要异步延迟调用。

then基本使用

》》》目标

p.then(onFulfilled, onRejected)

》》》 代码实现

then(onFulfilled, onRejected) {  // 异步操作完成修改状态后被调用, 根据状态决定调用的函数
    if (this.status === FULFILLED) onFulfilled(this.value)
    if (this.status === REJECTED) onRejected(this.value)
    if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
    }
}

》》》 使用

const p1 = new MyPromise((resolve, reject) => {
    resolve('成功了');
})

p1.then(res => console.log(res), err => console.log(err))     // 成功了


const p2 = new MyPromise((resolve, reject) => {
    reject('失败了');
})

p2.then(res => console.log(res), err => console.log(err))    // 失败了

then链式调用

  • then方法接收两个可选参数onFulfilledonRejected参数必须是函数

  • then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

  • 下一个then接收的参数就是上一个then返回的结果,上一个then没有返回值,则下一个then接收的是undefined

》》》 目标

p.then(function(){}, function(){})
 .then(function(){}, function(){})
 .then(function(){}, function(){})

》》》 代码实现

 // 异步操作完成修改状态后被调用,
  then(onFulfilled, onRejected) {  
      // 定义一个新的promise对象,因为then需要返回一个新的promise对象,才能被then链式调用
      const thenPromise = new Promise((resolve, reject) => {
          //  根据状态决定调用的函数
        if (this.status === FULFILLED) resolve(onFulfilled(this.value))
        if (this.status === REJECTED) reject(onRejected(this.value))
        // 等待状态则不能马上调用,先把回调存起来,等状态不是PENDING时再调用
        if (this.status === PENDING) {
            this.onFulfilledCallbacks.push(onFulfilled)
            this.onRejectedCallbacks.push(onRejected)
        }
      })
      //返回一个新的Promise对象
      return thenPromise
  }

》》》 使用

p.then((res) => { return 'result1' }, (err) => {})
 .then((res) => { console.log(res) }, (err) => {})  // 'result1'
 .then((res) => { console.log(res) }, (err) => {})  // 'undefined'
 .then((res) => { throw new Error('错误') }, (err) => {})
 .then((res) => {  }, (err) => { console.log(err) })   // Error: 错误

Promise类中处理异步逻辑(如定时器)

》》》 目标

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('异步setTimeout成功~~~!')
    reject('异步setTimeout成功~~~!')
  }, 1000)
})

p.then(res => console.log(res), err => console.log(err))  // '异步setTimeout成功~~~!'

处理异步逻辑时,可以先把then里的两个回调保存起来,等状态不是pending,就证明定时器/异步逻辑已执行完。建议使用数组保存这些回调,因为一个promise实例可能会有多次then

image.png

》》》代码实现

    onFulfilledCallbacks = []  // 保存成功回调
    onRejectedCallbacks = []  // 保存失败回调
    
  resolve(val){
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = val
    // 执行保存的成功回调
    while (this.onFulfilledCallbacks.length) {
      this.onFulfilledCallbacks.shift()(this.value)
    }
  }
  reject(err){
    if (this.status !== REJECTED) return
    this.status = REJECTED
    this.value = err
    // 执行保存的失败回调
    while (this.onRejectedCallbacks.length) {
      this.onRejectedCallbacks.shift()(this.value)
    }
  }
  
  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) onFulfilled(this.value)
    if (this.status === REJECTED) onRejected(this.value)
    if (this.status === PENDING) {
      // 处理异步待定状态,暂时保存两个回调
      this.onFulfilledCallbacks.push(onFulfilled.bind(this))
      this.onRejectedCallbacks.push(onRejected.bind(this))
    }
  }

Promise.all

all 接收的一个以Promise实例组成的数组,等待所有Promise实例执行成功,返回一个Promise实例传递数组结构的结果; 但凡其中有一个Promise实例操作失败,返回失败结果

》》》目标

1. 所有异步执行成功
const p1 = new Promise(resolve => resolve('resut1'))
const p2 = new Promise(resolve => resolve('resut2'))
const p3 = new Promise(resolve => resolve('resut3'))
const p4 = '123'

const all = Promise.all([p1, p2, p3, p4])
all.then(res => console.log(res))    // [ 'resut1', 'resut2', 'resut3', '123'  ]

2、异步操作有失败的
const p1 = new Promise(resolve => resolve('resut1'))
const p2 = new Promise((resolve, reject) => reject('error2'))
const p3 = new Promise(resolve => resolve('resut3'))

Promise.all([p1, p2,p3]).then(res => console.log(res)).catch(err => console.log(err))  // 'error2'

》》》 代码实现

 static all (arr) {  // 接收一个数组
    if(!isArray(arr)) {
      throw new Error('all 需要传递一个数组类型')
    }
    const allPromise = new MyPromise((resolve, reject) => {
      const newArr = []
      for(let i = 0; i<arr.length; i++){
        if(arr[i] instanceof MyPromise) {
          arr[i].then(success => {
            newArr.push(success)
          }, error => {
            reject(error)
          })
        } else {
        // 数组元素是非Promise实例的,直接放到数组里
          newArr.push(arr[i])
        }
        i === arr.length - 1 && (resolve(newArr))  // 判断是否已经完成
      }
    })
    // 返回一个新的Promise实例,传递数组结构的结果
    return allPromise
  }

》》》使用

const p1 = new MyPromise(resolve => resolve('resut1'))
const p2 = new MyPromise((resolve, reject) => reject('error2'))
const p3 = new MyPromise(resolve => resolve('resut3'))
const p4 = '123'

MyPromise.all([p1, p3, p4]).then(res => console.log(res), err => console.log(err))  // [ 'resut1', 'resut3', '123' ]

MyPromise.all([p1, p2, p3, p4]).then(res => console.log(res), err => console.log(err))  // 'error2'

Promise.race

方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

》》》目标

const p1 = new Promise(resolve => resolve('resut1'))
const p2 = new Promise(resolve => resolve('resut2'))
const p3 = new Promise(reject => reject('resut3'))


Promise.race([p1, p2, p3]).then(res => {
  console.log('success:', res); 
}, err => {
  console.log('error:', err);
})

//  success: resut1

》》》代码实现

  static race(arr) {   // 接收一个数组,数组元素必须是Promise实例,数组中但凡有一个操作执行成功/失败,就马上返回结果
    const racePromise = new MyPromise((resolve, reject) => {
      if(!isArray(arr)) {
        reject('race 需要传递一个数组类型')
        return
      }
      if (!arr.every(v => v instanceof MyPromise)){
        reject('race 需要传递一个由MyPromise实例组成的数组')
        return
      }
      const resulrArr = []
      for(let i = 0; i<arr.length; i++){
        if (resulrArr.length > 0) {
          resolve(resulrArr[0])
          return
        }
        arr[i].then(success => {
          resulrArr.push(success)
        }, error => {
          resulrArr.push(error)
        })
      }
    })
    // 返回一个新的Promise实例,传递第一个操作完成的返回结果(成功/失败)
    return racePromise
  }

》》》使用

const p1 = new MyPromise(resolve => resolve('resut1'))
const p2 = new MyPromise((resolve, reject) => reject('error2'))
const p3 = new MyPromise(resolve => resolve('resut3'))
const p4 = '123'

MyPromise.race([p1, p2, p3]).then(res => {
  console.log('success:', res);
}, err => {
  console.log('error:', err);
})

// success: resut1


MyPromise.race([p1, p2, p3, p4]).then(res => {
  console.log('success:', res);
}, err => {
  console.log('error:', err);
})

// error: race 需要传递一个由MyPromise实例组成的数组

Finally

  • 在 promise 执行完毕后无论其结果怎样都做一些处理或清理时,finally() 方法可能是有用的
  • 由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。

》》》 目标

const p = new Promise((resolve, reject) => {
    resolve('操作成功!')
    reject('操作成功!')
})


p.then(res => console.log(res)).finally(() => console.log('操作结束'))

//  操作成功!    
//  操作结束

》》》代码实现

  onFinallyCallbacks = []
  
finally(onFinally) {
    if(typeof onFinally !== 'function') throw new Error('MyPromise finally方法接收的参数必须是函数')
        const finallyPromise = new MyPromise((resolve, reject) => {
              if (this.status === FULFILLED || this.status === REJECTED) {
                resolve(onFinally)
              }
              if (this.status === PENDING){
                this.onFinallyCallbacks.push(onFinally)
              }
        })
    return finallyPromise
}
  

》》》使用

const p = new MyPromise((resolve, reject) => {
    resolve('操作成功!')
    reject('操作成功!')
})

p.then(res => console.log(res)).finally(() => console.log('操作结束'))

//  操作成功!    
//  操作结束