手动实现Promise

98 阅读4分钟

最近在网上看了一些实现Promise的相关内容,也总结一下自己对Promise实现的理解,它的核心原理类似“发布订阅模式

直接上代码:

// 判断是否为function  
const isFunction = val => typeof val === 'function'  

// 定义Promise的三种状态 
const PENDING = 'PENDING'  
const FULFILLED = 'FULFILLED' 
const REJECTED = 'REJECTED'
class Promise {
  constructor(handle) {    
    if (!isFunction(handle)) {  
      throw new Error('Promise must accept a function as a parameter')   
    }     
    // 状态    
    this.status = PENDING    
    // 值    
    this.value = undefined    
    // 成功回调函数队列   
    this.fulfilledQueues = []   
    // 失败回调函数队列   
    this.rejectedQueues = []   
    // 执行handle    
    try {       
      // 绑定一下this指向,不然箭头函数中执行会有问题
      handle(this.resolve.bind(this), this.reject.bind(this)) 
    } catch (err) { 
      this.reject(err)    
    }  
  }
  
  // resovle方法   
  resolve(val) {   
    const run = () => {  
      if (this.status !== PENDING) return 
      this.status = FULFILLED   
      // 依次执行成功队列中的函数,并清空队列     
      const runFulfilled = (res) => {   
        let cb;        
        while (cb = this.fulfilledQueues.shift()) { 
          cb(res)         
        }     
      }      
      // 依次执行失败队列中的函数,并清空队列  
      const runRejected = (error) => {  
        let cb;          
        while (cb = this.rejectedQueues.shift()) {   
          cb(error)    
        }       
      }       
      /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,  
      当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态      
      */       
      if (val instanceof Promise) {   
        val.then(res => {     
          this.value = res     
          runFulfilled(res)       
        }, err => {       
          this.value = err     
          runRejected(err)     
        })    
      } else {   
        this.value = val  
        runFulfilled(val)     
      }    
    }     
    // 为了支持同步的Promise,这里采用异步调用   
    setTimeout(run, 0)  
  }
  
  // reject方法 
  reject(err) {   
    if (this.status !== PENDING) return    
    // 依次执行失败队列中的函数,并清空队列   
    const run = () => {      
      this.status = REJECTED     
      this.value = err    
      let cb;      
      while (cb = this.rejectedQueues.shift()) {  
        cb(err)     
      }    
    }     
    // 为了支持同步的Promise,这里采用异步调用   
    setTimeout(run, 0)   
  }
  
  // then方法   
  then(onFulfilled, onRejected) {   
    const { value, status } = this     
    // 返回一个新的Promise对象   
    return new Promise((onFulfilledNext, onRejectedNext) => {  
      // 封装一个成功时执行的函数    
      let fulfilled = value => {        
        try {     
          if (!isFunction(onFulfilled)) {      
          onFulfilledNext(value)       
        } else {    
          let res = onFulfilled(value);      
          if (res instanceof Promise) {    
            // 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调  
            res.then(onFulfilledNext, onRejectedNext)  
          } else {    
            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数               
            onFulfilledNext(res)  
          }          
        }        
      } catch (err) {    
        // 如果函数执行出错,新的Promise对象的状态为失败 
        onRejectedNext(err)     
      }      
    }      
    // 封装一个失败时执行的函数   
    let rejected = error => {  
      try {        
        if (!isFunction(onRejected)) {    
          onRejectedNext(error)     
        } else {    
          let res = onRejected(error);   
          if (res instanceof Promise) {    
            // 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调                       res.then(onFulfilledNext, onRejectedNext)    
          } else {       
            //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数               
            onFulfilledNext(res)       
          }           
        }        
      } catch (err) {   
        // 如果函数执行出错,新的Promise对象的状态为失败      
        onRejectedNext(err)    
      }   
    }       
    switch (status) {   
      // 当状态为pending时,将then方法回调函数加入执行队列等待执行    
      case PENDING:       
        this.fulfilledQueues.push(fulfilled)   
        this.rejectedQueues.push(rejected)       
      break;         
      // 当状态已经改变时,立即执行对应的回调函数    
      // 下面两种情况是在直接调用Promise的静态方法resolve或者reject时,状态就会是FULFILLED或REJECTED 
      case FULFILLED: 
        fulfilled(value)       
      break;    
        case REJECTED:      
        rejected(value)  
      break;    
      }   
    })  
  }
  // catch方法(实际是调用了then的第二个参数)  
  catch(onRejected) { 
    return this.then(undefined, onRejected)   
  }
  
  // 静态resolve方法   
  static resolve(value) {     
    // 如果参数是Promise实例,直接返回这个实例   
    if (value instanceof Promise) return value   
    return new Promise(resolve => resolve(value))   
  }
  
  // 静态reject方法 
  static reject(value) {   
    return new Promise((resolve, reject) => reject(value))   
  }
    
  // 静态all方法
  static all(list) { 
    return new Promise((resolve, reject) => {   
    // 返回值的集合     
    let values = []  
    let count = 0    
    for (let [i, p] of list.entries()) {  
      // 数组参数如果不是Promise实例,先调用Promise.resolve 
      this.resolve(p).then(res => { 
        values[i] = res       
        count++         
        // 所有状态都变成fulfilled时返回的Promise状态就变成fulfilled   
        if (count === list.length) resolve(values)      
       }, err => {  
         // 有一个被rejected时返回的Promise状态就变成rejected   
         reject(err)       
       })    
     }  
   })   
 }
 
 // 静态race方法
 static race(list) {
   return new Promise((resolve, reject) => {    
     for (let p of list) {       
     // 只要有一个实例率先改变状态,新的Promise的状态就跟着改变   
     this.resolve(p).then(res => {     
       resolve(res)        
     }, err => {         
       reject(err)       
     })      
   }     
 })   
 }   
 
 // 静态finally方法
 finally(cb) {    
   return this.then(  
   value => Promise.resolve(cb()).then(() => value),  
   reason => Promise.resolve(cb()).then(() => {
     throw reason
     }) 
   );   
   }
}

例子解析:

let p = new Promise((resolve, reject) => {
  setTimeout(() => { 
    resolve('成功')    
  }, 1000)
}) 
p.then(res => { 
  console.log(res)   
  })

执行new Promise(fn)时,执行传入的回调函数(以Promise的resolve方法和reject方法作为参数), 所以在一秒后会执行resolve方法;因为resolve是在异步中执行的,所以在执行resolve()之前,已经执行了p.then()【只是then中的回调函数还没执行】,在执行then方法时,状态是pending,所以会将then的参数添加进对应的回调函数队列; 所以在一秒后执行resolve时,成功函数队列中有值,可以取出并执行它,也就是执行then中的回调函数