Promise源码解析(Promise A+)

355 阅读6分钟

一.Promise的三个状态以及executor执行器

1.1 Promise有三个状态分别时PENDING FULFILLED REJECTED

1.2 Promise接收一个参数executor也就是执行器executor 这个执行器是一个函数 并且接口两个参数 resolve reject这两个也是函数

1.3 由于我们是通过new Promise生成Promise对象 所以因为new内部原理 在new的时候会自动执行constructor这个构造函数,所以我们要在constructor中执行executor这个执行器 在executor函数体内去执行resolve reject方法 将内容传递出去

1.4 我们的then方法也接受两个参数 这两个参数也是函数 这两个函数的作用是根据状态去用指定的函数接收执行器函数块内传过来的内容

1.5 如果executor函数体内抛出的是异常我们需要利用try catch进行捕获 并且reject出去

const MyPromise = require('./MyPromise')
let promise = new Promise((resolve,reject)=>{
 resolve('value')
 *// reject('reason')*
 *// throw new Error('Error')*
})
promise.then((value)=>{
 console.log(value)
},(reason)=>{
 console.log(reason)
})
const PENDING = 'PENDING',  // Promise的三个状态 (1.1)
   FULFILLED = 'FULFILLED',
   REJECTED = 'REJECTED';
class MyPromise{
 constructor(executor){  *// 因为在new的时候内部会自动执行constructor (1.2)
  this.status = PENDING;
  this.value = undefined;
  this.reason = undefined;
  const resolve = (value)=>{
   if(this.status === PENDING){  // 因为执行了resolve 所以状态变为了FULFILLEDthis.status = FULFILLED;
​    this.value = value
   }
  }
  const reject = (reason)=>{
   if(this.status === PENDING){
​    this.status = REJECTEDthis.reason = reason
   }
  }
  try{  // 当我们去执行executor这个执行器的时候 函数体内可能有异常我们需要去捕获 (1.6)
   executor(resolve,reject) *// 执行executor executor函数块中会执行也就是调用resolve || reject || throw new Error这三部分的一个 (1.3)
  }catch(e){
   	reject(e) // 并且reject出去
  }
 then(onFulfilled,onRejected*){ *// then也接收两个函数参数 并且根据状态调用其中一个函数 (1.4)
  if(this.status === FULFILLED){
   onFulfilled(this.value)
  }
  if(this.status === REJECTED){
   onRejected(this.reason)
  }
 }
}
module.exports = MyPromise

二.发布者订阅者处理异步Promise

当我们Promise产生异步的resolve或者reject的时候我们应该如何处理

2.1 当我们new Promise的时候 执行器executor会自动执行 函数体内的resolve reject throw等也会执行并且改变Promise当前的状态 如果存在的话 但是当resolve reject异步的时候 executor自动执行的时候 并没有能够立即去执行resolve reject也就意味着并不会改变当前Promise的状态 所以我们就需要处理状态为PENDING的情况

2.2 当我们执行then方法 发现Promise的状态还是PENDING的时候我们就无法立即执行then中的两个函数 我们需要将then方法等待Promise状态改变后执行的两个函数订阅起来

2.3 等到延时到达的时候 executor执行器函数体内的resolve reject方法被执行的时候 我们再去发布订阅的函数

const MyPromise = require('./MyPromise')
let promise = new MyPromise((resolve,reject)=>{
 setTimeout(() => {  // 异步模式
  resolve('Success')
 }, 2000);
})
promise.then((value)=>{
 console.log(value)
},(reason)=>{
 console.log(reason)
})
const PENDING = 'PENDING',
   	FULFILLED = 'FULFILLED',
   	REJECTED = 'REJECTED';
class MyPromise{
 constructor(executor){ 
  this.status = PENDING;
  this.value = undefined;
  this.reason = undefined;
  // 用两个容器分别来装成功的回调函数和失败的回调函数*
  this.onFulfilledCallbacks = []
  this.onRejectedCallbacks = []
  const resolve = (value)=>{
   if(this.status === PENDING){
​    this.status = FULFILLED;
​    this.value = value
​    // 发布 等到延时到达 状态改变的时候 我们在执行*this.onFulfilledCallbacks.forEach(fn=>fn()) // (2.3)
   }
  }
  const reject = (reason)=>{
   if(this.status === PENDING){
​    this.status = REJECTEDthis.reason = reason
​    this.onRejectedCallbacks.forEach(fn=>fn())
   }
  }
  try{
   executor(resolve,reject) 
  }catch(e){
   reject(e)
  }
 }
 then(onFulfilled,onRejected){ 
  if(this.status === FULFILLED){
   onFulfilled(this.value)
  }
  if(this.status === REJECTED){
   onRejected(this.reason)
  }
  if(this.status === PENDING){ *// (2.1)
   *// 发布者订阅者模式*
   *// 订阅*
   this.onFulfilledCallbacks.push(()=>{  // (2.2)onFulfilled(this.value)
   })
   this.onRejectedCallbacks.push(()=>{
​    onRejected(this.reason)
   })
  }
 }
}
module.exports = MyPromise

三.Promise的链式回调以及then的处理

3.1 then方法其实返回的仍然是一个Promise对象,只有这样我们才能继续的链式调用.then.then下去否则是不可能的,所以我们需要在then方法内包裹一层Promise并return出去以便后续的链式调用

3.2 争对then方法内部onFulfilled或者onRejected方法执行后获得的x我们需要判断其的形式

​ 3.2.1 如果x是当前Promise对象

​ 3.2.2 如果x是一个普通的值

​ 3.2.3 如果x是Promise语法糖

​ 3.2.4 如果x是一个新的Promise

​ 3.2.5 如果函数内部抛出错误

3.3 确保当3.2.4的情况下如果resolve并且reject同时有的时候我们只运行一个

const MyPromise = require('./MyPromise')
let promise1 = new MyPromise((resolve,reject)=>{
  resolve('promise1')
})
// then方法会return 一个新的promise对象
let promise2 = promise1.then((value)=>{
  // return value + '->then->promise2'    // 3.2.2
  // return Promise.resolve(value + '->then->promise2')   // 3.2.3
  return new MyPromise((resolve,reject)=>{ // 3.2.4
    resolve(value + '->then->promise2')    // 3.3
    reject('溜了溜了')
  })
  // throw Error      // 3.2.5
  // return promise2  // 3.2.1
})
.then((value)=>{
  console.log(value)
})
const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';
class MyPromise{
  constructor(executor){   
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = (value)=>{
      if(this.status === PENDING){
        this.status = FULFILLED;
        this.value = value
        this.onFulfilledCallbacks.forEach(fn=>fn())
      }
    }
    const reject = (reason)=>{
      if(this.status === PENDING){
        this.status = REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn=>fn())
      }
    }
    try{
      executor(resolve,reject) 
    }catch(e){
      reject(e)
    }
  }
  then(onFulfilled,onRejected){  
    // 为什么要用新的promise包裹 因为then方法返回一个新的Promise
    // 因为then方法会return一个新的promise 因为这样你才能链式调用
    let promise2 = new MyPromise((resolve,reject)=>{   // (3.1)
      // 并且onFulfilled onRejected会返回一个X
      if(this.status === FULFILLED){
        setTimeout(()=>{
          try{  // 3.2.5
            let x = onFulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)  // 在内部这个promise2是无法拿到的 因为还没return出去 我们用异步的方式解决这个问题
          }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){ 
        this.onFulfilledCallbacks.push(()=>{
          try{
            let x = onFulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }    
        })
        this.onRejectedCallbacks.push(()=>{
          try{
            let x = onRejected(this.reason)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        })
      }
    })
    return promise2   // (3.1)
  }
}

function resolvePromise(promise2,x,resolve,reject){
  if(promise2 == x){
    // 3.2.1
    reject(new TypeError('CHAINING CYCLE'))
  }
  let called = false;  // 3.3
  if((typeof x === 'object' && x!==null) || typeof x === 'function'){
    try{  // 这里的try是为了处理x.then取值的时候可能报错
      let then = x.then  
      if(typeof then === 'function'){
         // 3.2.4
         //  return new MyPromise((resolve,reject)=>{
         //     resolve(value + '->then->promise2')
         //   })
        then.call(x,(y)=>{
         // 我们需要用then方法接收抛出的结果 然后resolve这个结果 所以使用call方法
          if(called) return 
          called = true;
          resolve(y)
        },(r)=>{
          if(called) return 
      	  called = true;
          reject(r)
        })
      }else{
        // 3.2.3
        // return Promise.resolve(value + '->then->promise2')
        resolve(x)
      }
    }catch(e){
      if(called) return 
      called = true;
      reject(e)
    }
  }else{
    // 3.2.2
    // return value + '->then->promise2'
    resolve(x)
  }
}
module.exports = MyPromise

四.Promise多层嵌套、链式调用与catch方法实现(Promise源码最终版)

4.1 当我们去深层次抛出Promise的时候 我们需要递归的去处理这些Promise

4.2 对于.then().then().then()这种没有传递参数的then我们需要去给定两个函数参数来穿透到最后

4.3 catch本质上就是一个then方法 但是它只抛出错误 所以也就是then(null,onRejected)这种形式 只使用第二个函数参数去抛出结果

const MyPromise = require('./MyPromise')
let promise1 = new MyPromise((resolve,reject)=>{
  resolve('promise1')
})
let promise2 = promise1.then((value)=>{
  return new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
      resolve(new MyPromise((resolve,reject)=>{
        resolve(new MyPromise((resolve)=>{
          resolve('递归')
        }))
      }))
    },2000)
  })
})
promise2.then().then().then().then().then((value)=>{
  throw new Error('Error')
},(reason)=>{
  console.log(reason)
}).catch((e)=>{
  console.log(e)
})
const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';
class MyPromise{
  constructor(executor){ 
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value)=>{
      if(this.status === PENDING){
        this.status = FULFILLED;
        this.value = value
        this.onFulfilledCallbacks.forEach(fn=>fn())
      }
    }
    const reject = (reason)=>{
      if(this.status === PENDING){
        this.status = REJECTED
        this.reason = reason

        this.onRejectedCallbacks.forEach(fn=>fn())
      }
    }

    try{
      executor(resolve,reject)  
    }catch(e){
      reject(e)
    }

  }
  then(onFulfilled,onRejected){ 
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=>value;  // 4.2.2
    onRejected = typeof onRejected === 'function' ? onRejected : reason=>{throw reason} // 对于reject我们只需要直接抛出错误就行
    let promise2 = new MyPromise((resolve,reject)=>{
      if(this.status === FULFILLED){
        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){ 
        this.onFulfilledCallbacks.push(()=>{
          try{
            let x = onFulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }    
        })
        this.onRejectedCallbacks.push(()=>{
          try{
            let x = onRejected(this.reason)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        })
      }
    })
    return promise2
  }
  catch(errorCallback){
    return this.then(null,errorCallback)  // 4.2.3
  }
}
function resolvePromise(promise2,x,resolve,reject){
  if(promise2 == x){
    reject(new TypeError('CHAINING CYCLE'))
  }
  let called = false;
  if((typeof x === 'object' && x!==null) || typeof x === 'function'){
    try{  
      let then = x.then  
      if(typeof then === 'function'){
        then.call(x,(y)=>{
          if(called) return 
          called = true;
          resolvePromise(promise2,y,resolve,reject)   // 4.2.1
        },(r)=>{
          if(called) return 
          called = true;
          reject(r)
        })
      }else{
        resolve(x)
      }
    }catch(e){
      if(called) return 
      called = true;
      reject(e)
    }
  }else{
    resolve(x)
  }
}

module.exports = MyPromise