手写 Promise

153 阅读7分钟

简单实现

  • 1.新建MyPromise类,传入执行器executor

image.png

  • 2.executor传入resolve和reject方法

image.png

  • 3.状态与结果的管理

image.png

image.png

image.png

image.png

  • 4.then简单实现

image.png

  • 5.使用module.exports对外暴露MyPromise类

image.png

在promise类中加入异步逻辑

  • 缓存成功与失败回调

image.png

  • then方法中的Pending的处理

image.png

  • resolve与reject中调用回调函数

image.png

image.png

实现then方法多次调用添加多个处理函数

Promise的then方法是可以被多次调用的。如果有多个then的调用,如果是同步回调,直接返回当前的值。如果是异步回调,那么保存的成功失败的回调,需要用不同的值保存,因为都互不相同。

  • MyPromise类中新增两个数组

image.png

  • 回调函数存入数组中

image.png

  • 循环调用成功和失败回调

image.png

image.png

实现then方法的链式调用

then方法要链式调用那么就需要返回一个Promise对象 then方法里面return一个返回值作为下一个then方法的参数,如果是return一个Promise对象,就需要判断它的状态

image.png

image.png

then方法链式调用识别Promise是否返回自己

如果then方法返回的是自己的Promise对象,则会发生循环调用,这个是否程序会报错

image.png

image.png

再次修改

image.png

捕捉错误

  • then执行时错误捕获

image.png

promise.js

// 自定义Promise函数模块: IIFE
(function(window){

/*
Promise构造函数
excutor:执行器函数(同步执行)
*/
function Promise(excutor){
  // 属性
  const _this = this //将当前promise对象保存起来
  _this.status = 'pending'//给promise对象指定status属性,初始值为pending
  _this.data = undefined //给promise对象指定一个用于存储结果数据的属性
  _this.callbacks = [] // 每个元素的结构:{onResolved(){},onRejected(){}}
  //resolve方法
  function resolve(value){
     
     if(_this.status !== 'pending'){ //如果当前状态不是pending,直接结束
         return
     }
     
     _this.status = 'resolved' //将状态改为resolved
     
     _this.data= value //保存value 数据
     
     if(_this.callbacks.length > 0){//如果有待执行callback函数,立即异步执行回调函数onResolved
         setTimeout(() => {
             _this.callbacks.forEach(callbacksObj => {
             callbacksObj.onResolved(value)
         })
     })
     }
  }
  // reject方法
  function reject(reason){
      
       if(_this.status !== 'pending'){//如果当前状态不是pending,直接结束
             return
       }
     
     _this.status = 'rejected'//将状态改为rejected
     
     _this.data= reason//保存value 数据
     
     if(_this.callbacks.length > 0){//如果有待执行callback函数,立即异步执行回调函数onRejected
         setTimeout(() => {
             _this.callbacks.forEach(callbacksObj => {
             callbacksObj.onRejected(reason)
         })
     })
     }}
//立即同步执行executor
  try{
     excutor(resolve,reject)
   }catch(error){//如果执行器跑出异常,promise对象变为rejected状态
   reject(error)
 }
}
/*
Promise原型对象的then()
指定成功和失败的回调函数
返回一个新的promise对象
*/
Promise.prototype.then = function(onResolved,onRejected){
    const _this = this
    
   // 返回一个新的promise对象
   return new Promise((resolve,reject)=>{
     if(_this.status === 'pending'){

     //假设当前状态是pending,
     _this.callbacks.push({
        onResolved,
        onRejected
     })
     }else if(_this.status ==='resolved'){
       setTimeout(()=> {
         /*
         1:如果跑出异常,return的promise就会失败,reason就是error
         2:如果回调函数返回不是promise,return的promise就会成功,value就是返回的值
         3:如果回调函数返回是promise,return的promise的结果就是这个promise的结果
         */
         try {
            const result = onResolved(_this.data)
            if(result instanceof Promise){
              result.then(
                 value =>resolve(value), //当result成功时,让return的promise也成功
                 reason => reject()//当result失败时,让return的promise失败
                 
              )
            } else {
              resolve(result)
            } catch (error){
              reject(error)
            }
         }
          
       })
     }else {
        setTimeout(()=> {
          onRejected(_this.data)
       })
     }
   })
}
/*
Promise原型对象的catch()
指定失败的回调函数
返回一个新的promise对象
*/
Promise.prototype.catch = function (onRejected){
}
/*
Promise函数对象的resolve方法
返回一个指定结果的成功的promise
*/
Promise.resolve = function (value){
}
/*
Promise函数对象的reject方法
返回一个指定reason的失败的promise
*/
Promise.reject = function (reason){
}
/*
Promise函数对象的all方法
返回一个promise,只有当所有promise都成功时才成功,否则只要有一个失败就返回失败
*/
Promise.all = function (promises){
}
/*
Promise函数对象的race方法
返回一个promise,其结果有第一个完成的promise决定
*/
Promise.race = function (promises){
}
//向外暴露Promise函数
window.Promise = Promise
})(window)

// html

<script src="./promise.js">
<script>
const p = new Promise((resolve,reject) => {
   setTimeout(()=> {
       resolve(1)
   },1000)
})
p.then(
   value =>{
      console.log('onResolved1()',value)
   },
   reason => {
      console.log('onRejected()',reason)
   }

)
</script>

Promise基础面试题

  • Promise状态改变后,promise.then才会执行
const promise = new Promise((resolve,reject)=>{
  console.log('1')
});
promise.then(()=>{
  console.log('2')
})
console.log('3')

结果:

1 3

  • new Promise()是否包裹在函数当中
const fn =() =>(new Promise((resolve,reject) =>{
   console.log('1')
   resolve('success')
}))
fn().then(res =>{
   console.log(res)
})
console.log('start')

解析:fn函数是直接返回一个new Promise的,而且fn函数的调用时在start之前,所以它里面的内容应该会先执行 结果:

1 start success

  • 如果把fn的调用放在start之后
const fn =() =>
new Promise((resolve,reject) =>{
   console.log('1')
   resolve('success')
})
console.log('start')
fn().then(res =>{
   console.log(res)
})

解析:new Promise()是包裹在函数当中的,只有在函数调用的时候才会执行 结果:

start 1 success

Promise结合setTimeout

const promise1 = new Promise((resolve,reject)=>{
   setTimeout(()=>{
      resolve('success')
   },1000)
})
const promise2 = promise1.then(()=>{
   throw new Error('error')
})
console.log('promise1',promise1)
console.log('promise2',promise2)
setTimeout(()=>{
   console.log('promise1',promise1)
   console.log('promise2',promise2)
},2000)

结果:

 'promise1'  Promise{ <pending> }

 'promise2' Promise{<pending>}

 'promise1' Promise{<resolved>:'success'}

 'promise2' Promise{<rejected>:Error:error!!!}

解析:关键步骤: 先执行第一个定时器里的内容,将promise1的状态改为resolved且保存结果并将之前的promise1.then推入微任务队列

该定时器中没有其它的同步代码可执行,因此执行本轮的微任务队列,也就是promise1.then,它抛出了一个错误,且将promise2的状态设置为rejected

Promise中的then、catch、finally

总结:

  • 1:Promise的状态一经改变就不能再改变
const promise = new Promise((resolve,reject)=>{
   resplve('success1')
   reject('error')
   resolve('success2')
})
promise.then(res=>{
  console.log('then',res)
}).catch(err =>{
  console.log('catch',err)
})

// 结果
then success1

构造函数中的resolve或reject只有第一次执行有效,多次调用没有任何作用。
  • 2:.then和.catch都会返回一个新的Promise
  • 3:catch不管被连接到哪里,都能捕获上层未捕捉过的错误。catch()也会返回一个promise,由于这个promise没有返回值,所以是undefined
const promise = new Promise((resolve,reject)=>{
   reject('error')
   resolve('success1')
})
promise.then(res =>{
  console.log('then1',res)
}).then(res => {
  console.log('then2',res)
}).catch(err=>{
 console.log('catch',err)
}).then(res=>{
 console.log('then3',res)
})
// 结果
catch, error
then3,undefined

解析:
1catch不管被连接到哪里,都能捕获上层未捕捉过的错误
2catch()也会返回一个promise,由于这个promise没有返回值,所以是undefined
  • 4:在promise中,返回任意一个非Promise的值都会被包裹成promise对象
  • 5:Promise的.then或者.catch可以被调用多次,但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。
Promise.resolve(1)
.then(res => {
   console.log(res)
   return 2    // resolve(2)
})
.catch(err =>{
  return 3
})
.then(res =>{
  console.log(res)
})
// 结果;1  2
解析:
Promise可以链式调用,不过promise每次调用.then或.catch都会返回一个新的promise,从而实现了链式调用。
  • 6:.then或者.catch中return一个error对象并不会抛出错误,所以不会被后续的.catch捕获
Promise.resolve().then(()=>{
  return new Error('error')
}).then(res =>{
  console.log('then',res)
}).catch(err=>{
console.log('catch',err)
})
// 结果:then,Error:error
解析:返回任意一个非promise的值都会被包裹成promise对象
return new Error('error')会被包裹成return Promise.resolve(new Error('error'))
  • 7:.then或.catch返回的值不能是promise本身,否则会造成死循环
const promise = Promise.resolve().then(()=>{
  return promise
})
promise.catch(console.err)

// 结果:报错,出现死循环
  • 8:.then或者.catch的参数期望是函数,传入非函数则会发生值透传
Promise.resolve(1)
   .then(2)
   .then(Promise.resolve(3))
   .then(console.log)
//结果:1
解析:.then.catch的参数期望是函数,传入非函数则会发生值透传
  • 9:.then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候可以认为catch是.then第二个参数的简便写法
Promise.reject('err!!!')
.then((res)=>{
  console.log('success',res)
},(err)=>{
  console.log('error',err)
}).catch(err=>{
 console.log('catch',err)
})
//结果:'error' 'error!!!'
// 解析:
.then函数中的两个参数,第一个参数是用来处理Promise成功的函数。第二个是处理失败的函数。
Promise.resolve('1')的值会进入成功的函数
Promise.reject('2')会进入失败的函数
  • 10:.finally方法也是返回一个Promise,它在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。

Promise中的all和race

  • .all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调
  • .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他方法仍在执行,不过执行结果会被抛弃