最最最细致的Promise拆解、手写秘籍!!你值得拥有

589 阅读31分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」      ,赢取创作大礼包,挑战创作激励金

Promise 核心逻辑实现

387ef957c975fc130bdbbdaaf145fa22.gif 本文的目标,通过一点点的拆解了解Promise的原理,从而达到我们手写Promise的目的

需求整理

巧妇难为无米之炊,在我们手撸代码之前,我们需要先整理一下Promise如何使用,Promise的一些特性,方便我们更好的进行代码的编写,所以我们要先知道啥是Promise

be233cf99e9e5e05285d875f4628de81.gif

1.调用Promise

调用Promise,我们就需要创建一个Promise对象,首先我们要通过 new 关键字进行创建

new Promise()

从这段代码中我们可以发现Promise是一个类

2.入参

调用Promise我们需要传入一个回调函数,我们称之为执行器 ,这个执行器会立刻执行,也就是说当我们在执行new Promise(fn)的时候,函数中的fn会被立刻调用

new Promise(() => {

})

3.执行器参数

当我们传入一个回调函数的时候,会获得两个参数resolvereject,这两个参数实际上是两个函数,调用这两个函数会改变Promise的状态。

new Promise((resolve,reject) => {

})

4.Promise的状态

Promise中有三种状态,分别是:

  • 成功 fulfilled
  • 失败 rejected
  • 等待 pending

5.Promise的状态改变特点

Promise的状态变更只能是以下两种情况:

  • 等待转变为成功( pending => fulfilled
  • 等待转变为失败( pending => rejected【注意:】 一旦状态确定就不可更改

想不到吧.jpg

6. 如何修改Promise的状态?传递成功或失败的值?

可以通过resolvereject进行状态的修改 resolve:

new Promise((resolve,reject) => {
    resolve('成功')
})

reject:

new Promise((resolve,reject) => {
    reject(失败)
})

7.then方法

这里给一个链接: Promise.prototype.then() 看看这个链接,然后我们整理一下需求

then() 方法返回一个 Promise (en-US)。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

那也就是说then方法内部做的事情就是判断状态,根据状态的不同调用不同的函数,每个Promise都可以调用到then方法,也就是说这个方法是原型链上的方法

let promise = new Promise((resolve,reject) => {
    reject(失败)
})
promise.then(()=>{}, ()=>{})

8.then的回调参数

我们先看看以下代码

let promise = new Promise((resolve,reject) => {
    reject(失败)
})
// promise.then(onFulfilled[, onRejected]);
promise.then(value => { // fulfillment }, reason => { // rejection});

结合我之前发的那个文档链接我们可以得出以下结论: then的成功和失败回调,都有一个参数表示成功/失败的值

需求整理

整理一下我们的需求:

  • Promise是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行
  • Promise中的回调函数会获得两个参数resolvereject,这两个参数实际上是两个函数
  • Promise中有三种状态分别为:
    • 成功 fulfilled
    • 失败 rejected
    • 等待 pending
  • Promise只能通过resolvereject进行状态的修改,且通过这两个函数进行数据的传递
  • Promise的状态变更一旦完成之后,就不能修改且Promise的状态变更只能有两种情况:
    • 等待转变为成功( pending => fulfilled
    • 等待转变为失败( pending => rejected
  • Promisethen方法根据状态的不同调用不同的函数且个方法是原型链上的方法
  • Promisethen方法成功和失败回调,都有一个参数表示成功/失败的值

记笔记.jpg

开始编写

1. Promise是一个类

简简单单的第一步,Promise的一小步,我这个彩笔程序员的的一大步

class MyPromise {
    
}

2.需要传入一个回调函数

这个我们使用类中的constructor进行接收,constructor 是一种用于创建和初始化class创建的对象的特殊方法。

class MyPromise {
    constructor(callback) {
    
    }
}

3.回调函数会立刻执行

class MyPromise {
    constructor(callback) {
        callback()
    }
}

4. Promise中的回调函数有两个为函数的参数resolvereject

这里我们就需要为 callback中传入resolvereject,且在MyPromise定义resolvereject函数,这里我们定义为箭头函数

class MyPromise {
    ...
    resolve = () => {
    
    }
    reject = () => {
    
    }
}

Q: 这里我们为什么要定义为箭头函数呢?.jpg A: 我们回忆一下我们该怎么调用这两个函数

new Promise((resolve,reject) => {
    resolve('成功')
})

new Promise((resolve,reject) => {
    reject(失败)
})

是不是都是这样直接调用的?如果这个函数是一个普通函数的话,那他的this指向是不是就是window或者undefined?

我们实验一下

普通函数: image.png

箭头函数: image.png 这里我们来复习一个知识点:

普通函数的this指向是在调用的时候确定的,而箭头函数的this指向是在定义的时候确定的!

5.Promise中有三种状态

我们需要定义状态的常量,还有用来改变的状态值变量并且让他默认等于等待(pending

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    ...
}

Q: 定义常量的好处是啥?

da0f8e9f2cdabfb1ad82f4d154b56192.jpg

A: 咳咳!主要是为了代码的复用(颤音)

6.Promise通过resolvereject进行状态的修改,且通过这两个函数进行数据的传递

这里我们就需要对resolvereject进行修改

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    // Promise 状态
    status = PENDING
    // 成功的值
    value = undefined
    // 失败的值
    reason = undefined
    constructor(callback) {
        callback(this.resolve,this.reject)
    }
    resolve = value => {
        // 将状态更改为成功
        this.status = FULFILLED
        // 保存成功的值
        this.value = value
    }
    reject = value => {
        // 将状态更改为失败
        this.status = REJECTED
        // 保存失败的值
        this.reason = value
    }
}

7.Promise的状态变更只能有两种情况(完成之后不能进行修改)

这里我们主要还是对resolvereject进行修改,我们需要进行判断status当前的状态如何,是否为等待,如果是进行修改状态,如果不是就不能进行修改

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    ...
    resolve = () => {
        // 如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING){
            return
        }
        // 将状态更改为成功
        this.status = FULFILLED
        // 保存成功的值
        this.value = value
    }
    reject = () => {
        // 如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING){
            return
        }
        // 将状态更改为失败
        this.status = REJECTED
        // 保存失败的值 
        this.reason = value
    }
}

8.Promisethen方法根据状态的不同调用不同的函数且个方法是原型链上的方法

那我们需要在类中定义then方法,且进行判断,如果状态是成功的就调用成功的回调,失败就调用失败的回调

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    ...
    then( successCallback, failCallback) {
        // 如果是成功的状态
        if(this.status === FULFILLED){
            successCallback()
        }else if(this.status === REJECTED){
            failCallback()
        }
    }
}

9.Promisethen方法成功和失败回调,都有一个参数表示成功/失败的值

我们需要在then的时候,将成功/失败的值传递回去

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    ...
    then( successCallback, failCallback) {
        // 如果是成功的状态
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }
    }
}

到这里我们的核心模块就实现完毕了 测试一下

image.png

Promise 核心逻辑实现总结

敲黑板 敲黑板 敲黑板 总结.jpg

  • Promise是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行
  • Promise中的回调函数会获得两个参数resolvereject,这两个参数实际上是两个函数
  • Promise中有三种状态分别为:
    • 成功 fulfilled
    • 失败 rejected
    • 等待 pending
  • Promise只能通过resolvereject进行状态的修改,且通过这两个值进行状态保存
  • Promise的状态变更一旦完成之后,就不能修改且Promise的状态变更只能有两种情况:
    • 等待转变为成功( pending => fulfilled )
    • 等待转变为失败( pending => rejected )
  • Promisethen方法根据状态的不同调用不同的函数且个方法是原型链上的方法
  • Promisethen方法成功和失败回调,都有一个参数表示成功/失败的值

完整代码

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    // Promise 状态
    status = PENDING
    // 成功的值 
    value = undefined 
    // 失败的值 
    reason = undefined
    constructor(callback) {
        callback(this.resolve,this.reject)
    }
    resolve = value => {
        // 如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING){
            return
        }
        // 将状态更改为成功
        this.status = FULFILLED
        // 保存成功的值
        this.value = value
    }
    reject = value => {
        // 如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING){
            return
        }
        // 将状态更改为失败
        this.status = REJECTED
        // 保存失败的值
        this.reason = value
    }
    then( successCallback, failCallback) {
        // 如果是成功的状态
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }
    }
}

Promise加入异步逻辑

众所周知Promise中,我们主要解决了异步的调用的一些问题,但是,就我们目前的代码而言,使用异步还是有一些问题的

首先根据我们上一篇整理的需求:

Promise是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行

然后看看下面这个执行

let promise1 = new MyPromise((resolve,reject) => {
    setTimeOut(() =>{
        resolve(1)
    },2000)
})

我们的回调函数会立刻执行,但是改变状态的resolve方法却包含在异步任务中,导致我们的then模块的调用懵逼了,状态对不上啊!

then( successCallback, failCallback) {
    // 如果是成功的状态
    if(this.status === FULFILLED){
        successCallback(this.value)
    }else if(this.status === REJECTED){
        failCallback(this.reason)
    }
}

then模块此时的心情

src=http___p9.itc.cn_q_70_images03_20210801_b1a31b8941884311a3176ce950ed5253.gif&refer=http___p9.itc.gif 所以我们要进行一下对then方法的改造

改造then方法

  1. 我们需要在判断添加 等待的时候的情况
    • 解决方法: 添加一个else判断即可
  2. 等待情况下,我们要临时存储失败/成功的回调
    • 解决方法: 添加来个实例属性,存储成功/失败的回调

代码实现

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
    ...
    
    // 成功回调
    successCallback = undefined
    // 失败回调
    failCallback = undefined
    
    ...
    then( successCallback, failCallback) {
        // 如果是成功的状态
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }else{
        // 等待的情况
            this.successCallback = successCallback
            this.failCallback = failCallback
        }
    }
}

但是这这是解决了 then的时候的存储问题,还没有解决异步函数执行完毕的执行问题啊?

接着往下看!

异步执行完毕的回调

异步状态的时候,我们还是会执行在resolvereject,所以在执行到resolvereject时,我们在resolvereject中调用一下我们保存下来的方法就好

代码实现

resolve = value => {
    // 如果状态不是等待,阻止程序向下执行
    if(this.status !== PENDING){
        return
    }
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功的值
    this.value = value
    // 判断成功回调是否存在,如果存在 调用
    this.successCallback && this.successCallback(this.value)
}
reject = value => {
    // 如果状态不是等待,阻止程序向下执行
    if(this.status !== PENDING){
        return
    }
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败的值
    this.reason = value
    // 判断失败回调是否存在,如果存在 调用
    this.failCallback && this.failCallback(this.reason)
}

ok这里我们就执行完毕了

测试一下

const MyPromise = require('./myPromise')
let promise1 = new MyPromise((resolve,reject) => {
  setTimeout(() =>{
    resolve('成功')
  })
})
promise1.then(value=>{
  console.log('success',value)
},reasons => {
  console.log('failure',reasons)
})

image.png 欸,没问题

总结.jpg 我们异步函数的处理就完毕了,接下来进入下一章节

完整代码

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
  // Promise 状态
  status = PENDING
  // 成功的值 
  value = undefined
  // 失败的值 
  reason = undefined
  // 成功回调
  successCallback = undefined
  // 失败回调
  failCallback = undefined
  constructor (callback) {
    callback(this.resolve, this.reject)
  }
  resolve = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功的值
    this.value = value
    // 判断成功回调是否存在,如果存在 调用
    this.successCallback && this.successCallback(this.value)
  }
  reject = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败的值
    this.reason = value
    // 判断失败回调是否存在,如果存在 调用
    this.failCallback && this.failCallback(this.reason)
  }
  then (successCallback, failCallback) {
    // 如果是成功的状态
    if (this.status === FULFILLED) {
      console.log('test')
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 等待的情况
      this.successCallback = successCallback
      this.failCallback = failCallback
    }
  }
}

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

提出问题

现在又有一个新问题来了,类似下方的代码一样,我们异步的情况下可以进行多次的执行promise1,可以多次输出

代码测试

let promise1 = new Promise((resolve,reject) => {
  setTimeout(() =>{
    resolve('成功')
  },2000)
})
// 第一次
promise1.then(value=>{
  console.log('success',value)
},reasons => {
  console.log('failure',reasons)
})
// 第二次
promise1.then(value=>{
  console.log('success',value)
},reasons => {
  console.log('failure',reasons)
})
// 第三次
promise1.then(value=>{
  console.log('success',value)
},reasons => {
  console.log('failure',reasons)
})

image.png

手写类myPromise测试

image.png

我们实现的类做不到!怎么办?

我们遇到什么困难,都不要怕,微笑着面对它,消除恐惧的最好办法就是面对恐惧、坚持才是胜利、加油、奥利给!

a8b9cdf034edc5354172380e0e35f311.gif

找到问题发生处

首先我们定位一下问题,为什么我们的只执行一次呢?在我们细细的查看代码之后,发现了病因所在

then (successCallback, failCallback) {
    // 如果是成功的状态
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 等待的情况
      this.successCallback = successCallback
      this.failCallback = failCallback
    }
  }

为什么出现这样的情况

首先在我们每次执行then的时候,并没有把多次的都存储起来,就导致了一个问题,在我们多次调用的时候,异步的情况下(堆栈的问题了,不太了解的同学稍微百度一下哦),successCallbackfailCallback保存的都是最后一次的值,导致了其他的值并没有被执行到,如图:

image.png ps: 同步情况以及没问题的啦,大家可以测试一下

解决问题的方法

定位了病根,那我们的解决就很方便了:

  • 改造then函数的失败/成功方法的存储 =>可更改为数组进行存储就好了
  • 改造 resolverejectsuccessCallbackfailCallback的执行方式 => 循环存储的数组然后执行

改造then函数

successCallbackfailCallback要更改为数组

class MyPromise {
    ...
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []
  ...
  then (successCallback, failCallback) {
    // 如果是成功的状态
    if (this.status === FULFILLED) {
      console.log('test')
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 等待的情况
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
 }

ok,这里以及修改完毕了!

改造 resolvereject函数

resolve:

resolve = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功的值
    this.value = value
    // 判断成功回调是否存在,如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    while(this.successCallback.length){
        // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
        this.successCallback.shift()(this.value)
    }
  }

reject:

reject = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败的值
    this.reason = value
    // 判断失败回调是否存在,如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while(this.failCallback.length){
        // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
        this.failCallback.shift()(this.reason)
    }
  }

到这里就解决完毕了

测试

既然做完了看到要进行以下测试,不然怎么验证我们是正确的

测试用例

const myPromise = require('./myPromise')

let promise1 = new myPromise((resolve, reject) => {
  setTimeout(() =>{
    reject('失败')
  },1000)
})
promise1.then(value =>{
  console.log(value)
},reason => {
  console.log(1)
  console.log(reason)
})
promise1.then(value =>{
  console.log(value)
},reason => {
  console.log(2)
  console.log(reason)
})
promise1.then(value =>{
  console.log(value)
},reason => {
  console.log(3)
  console.log(reason)
})

测试结果

image.png 完美,其他的用例就不进行截图了,太多了,大家有兴趣自己去测试一下哈!

总结

本小结主要是为了解决Promise下的then方法可以多次调用,当他的状态变化的时候,内部的方法依次执行,需要区分同步和异步的情况

  • 异步的情况,我们需要存储起来方法,然后进行依次调用
  • 同步的情况,我们只需要根据状态调用对应的方法就好

完整代码

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
  // Promise 状态
  status = PENDING
  // 成功的值 
  value = undefined
  // 失败的值 
  reason = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []
  constructor (callback) {
    callback(this.resolve, this.reject)
  }
  resolve = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功的值
    this.value = value
    // 判断成功回调是否存在,如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    while(this.successCallback.length){
        // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
        this.successCallback.shift()(this.value)
    }
  }
  reject = value => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return
    }
    // 将状态更改为失败
    this.status = REJECTED
    // 保存失败的值
    this.reason = value
    // 判断失败回调是否存在,如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while(this.failCallback.length){
        // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
        this.failCallback.shift()(this.reason)
    }
  }
  then (successCallback, failCallback) {
    // 如果是成功的状态
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 等待的情况
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
}

Promise实现then方法的链式调用

需求整理

Promise下面的then方法是可以链式调用的,下一个then方法的返回值其实是上一个then方法的回调函数的返回值,我们来看几个案例

1.链式调用第一个函数没有返回值:

代码:

let promise1 = new Promise((resolve, reject) => {
  resolve('成功')
})
promise1.then(val => {
  console.log(val)
}).then(val =>{
  console.log(val)
})

结果:

image.png

2.链式调用第一个函数有返回值:

代码:

let promise1 = new Promise((resolve, reject) => {
  resolve('成功')
})
promise1.then(val => {
  console.log(val)
  return 100
}).then(val =>{
  console.log(val)
})

结果:

image.png

所以可以确定:下一个then方法的返回值其实是上一个then方法的回调函数的返回值

这样一来我们需要做两件事情:

  1. then方法的链式调用
  2. 把上一个的返回值传递给下一个then方法

实现then方法的链式调用

首先我们需要明确一点then是在Promise下,如果说我们想要实现then方法的链式调用,那么每个then方法都应该返回一个Promise对象,如果then都返回一个Promise对象,这样才方便我们链式调用

第一步

既然我们要返回一个Promise,那我们就在then方法里面返回一个Promise

then (successCallback, failCallback) {
    let promiseThen = new MyPromise()
    // 如果是成功的状态
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 等待的情况
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
    return promiseThen
  }

第二步

既然已经返回了,那我们是不是就需要对判断进行一些修改呢? 我们回顾一下,在我们执行new Promise的时候,可以传入一个回调函数进去,且这个回调函数是立即执行的!

那么,我有一个不成熟的想法!将我们then内部的判断传入MyPromise中去执行!

then (successCallback, failCallback) {
    let promiseThen = new MyPromise(() => {
        // 如果是成功的状态
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallback(this.reason)
        } else {
            // 等待的情况
            this.successCallback.push(successCallback)
            this.failCallback.push(failCallback)
        }
    })
    return promiseThen
}

到这里我们的then方法的链式调用就编写完毕了

then方法值的传递

首先我们需要保存他执行的返回值,这个就非常简单了,我们只要保存执行的时候的返回值就好

let x = successCallback(this.value)

下一个问题就来,怎么传递数据呢?

别急再让我们回顾一下!

Promise通过resolvereject进行状态的修改,且通过这两个函数进行数据的传递

那么我们就可以通过调用resolve将返回值传递给我们返回出去的promise数据,也就能实现把上一个的返回值传递给下一个then方法

let promiseThen = new MyPromise((resolve, reject) => {
    // 如果是成功的状态
    if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolve(x)
    } else if (this.status === REJECTED) {
        failCallback(this.reason)
    } else {
        // 等待的情况
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
    }
})
return promiseThen

结束了吗?不还没有

到这里我们的判断似乎就结束了,但是!还没有结束,如果返回的是一个Promise对象怎么办?这个对象成功或者失败又该怎么办?撸起袖子加油干!

 if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolve(x)
    }

这里的resolve(x)就不能这样写了!要做一些修改,添加一些判断,什么判断呢?

  • 判断x是否promise对象
  • 如果不是 直接调用 reslove 函数
  • 如果是Promise对象根据其结果选择调用resolvereject函数

image.png

考虑到之后的代码复用性,我们把这个判断语句提取出来

// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise(x, resolve, reject){
  if(x instanceof MyPromise){
    x.then(resolve, reject)
  }else{
    resolve(x)
  }

}

然后对then中进行一下修改

then (successCallback, failCallback) {
    let promiseThen = new MyPromise((resolve, reject) => {
      // 如果是成功的状态
      if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolvePromise(x, resolve, reject)
      }
      ...
    })
    return promiseThen
  }

这样子我们就处理好了同步的情况,当然异步的情况也是要处理的啦!

思路是一样的

... 
 // 等待的情况
this.successCallback.push(() => {
  try {
    let x = successCallback(this.value)
    resolvePromise(x, resolve, reject)
  } catch (e) {
    reject(e)
  }
})
this.failCallback.push(() => {
  try {
    let x = failCallback(this.reason)
    resolvePromise(x, resolve, reject)
  } catch (e) {
    reject(e)
  }

})

这里我们加入了catch防止执行报错,当然之前的执行也需要添加,ok进入测试模式

测试

测试同步链式调用

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) => {
  resolve('成功')
})
let promise1 = new myPromise((resolve, reject) => {
  resolve('成功1')
})
promise.then(val => {
  console.log('第一次',val)
  return promise1
}).then(val =>{
  console.log('第二次',val)
  return promise
}).then(val =>{
  console.log('第三次',val)
  return promise1
}).then(val =>{
  console.log('第四次',val)
})

预计输出:

  • 第一次 成功
  • 第二次 成功1
  • 第三次 成功
  • 第四次 成功1

同步链式调用测试结果

image.png

异步链式调用测试

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功 1s 延迟版')
  },1000)
})
let promise1 = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功 0.5延迟版')
  },500)
})
promise.then(val => {
  console.log('第一次',val)
  return promise1
}).then(val =>{
  console.log('第二次',val)
  return promise
}).then(val =>{
  console.log('第三次',val)
  return promise1
}).then(val =>{
  console.log('第四次',val)
})

预计输出:

  • 第一次 成功 1s 延迟版
  • 第二次 成功 0.5延迟版
  • 第三次 成功 1s 延迟版
  • 第四次 成功 0.5延迟版

异步链式调用测试结果

image.png 都没问题 okok,解决

总结

then的链式调用主要是因为我们返回了同样的类,让函数可以继续调用then方法,在我们编写的时候要注意函数的异步和同步的调用,还有注意我们的方法选择哦!

Promisethen方法链式调用识别 Promise 对象自返回

我们在使用then方法的时候是可以返回一个对象的,但是我们如果返回原来的Promise就会造成一个事情,Promise的循环调用,造成系统bug,我们要避免这种情况,演示这种报错

使用系统的promise对象进行报错模拟

let promise = new Promise((resolve, reject) => {
    resolve(100)
})
let promise1 = promise.then((value) =>{
    console.log(value)
    return promise1
})

结果

image.png ok,到这里我们的需求就来了,我们该怎么处理这种自己调用自己的情况呢?

实现

实际上也非常简单,我们进入then方法,还记得我们之前的代码吗?有这么一段let x = successCallback(this.value)对吧,有了这一段就很简单了,我们只需要判断 x和我们链式调用返回的thenPromise是否相同即可!本次改造我们选择resolvePromise进行

resolvePromise的改造

resolvePromise新增一个thenPromise的入参进行判断,然后如果相同的话,我们就抛出错误即可

function resolvePromise (thenPromise,returnValue, resolve, reject) {
    if(thenPromise === returnValue){
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
    }
    if (returnValue instanceof MyPromise) {
        returnValue.then(resolve, reject)
    } else {
        resolve(returnValue)
    }
}

这里机制的同学就知道然后对应改造一下调用方法的地方,添加一下传参就好啦!

但是问题没有想象中那么简单!

我们看这里的代码

let thenPromise = new MyPromise((resolve, reject) => {
   ...
   let x = successCallback(this.value)
   resolvePromise(thenPromise, x, resolve, reject)
   ...
})

这里我们需要在 resolvePromise(thenPromise, x, resolve, reject)传入thenPromise,但是thenPromise的声明是在new MyPromise完毕之后才有的,这样回取不到值的!该怎么办???其实也简单! 异步调用!!! setTimeout!!!!是不是一下就茅塞顿开?开始改造吧!

自己就改改之后贴代码合集

测试

测试代码

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) => {
    resolve('成功')
})
let promise1 = promise.then(val => {
  console.log(val)
  return promise1
})
promise1.then(val => {
}, reason => {
  console.log(reason.message)
})

测试结果

image.png 完美

then方法参数变为可选参数

let promise3 = new Promise((resolve, reject) => {
  resolve('失败')
})
promise3.then().then().then( value => console.log(value))

Promise是可以这样不断传递参数直到有回调方法的情况

该怎么实现呢? 很简单的,我们可以这样理解,在没有参数的时候,我们就给他传入一个 value => value的函数不就好了??? 完美利用特性

上代码

// 创建一个变量
const isFunction = (value) => typeof value === "function";
....
...
then(successCallback, failCallback) {
    successCallback = isFunction(successCallback)
      ? successCallback
      : (value) => value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) => {
          throw err;
        };
      ....
}
....

测试一下

测试用例

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  },2000)
})
let promise1 = new myPromise((resolve, reject) => {
  setTimeout(() => {
    reject('失败')
  },2000)
})
promise.then().then().then( value => console.log(value,1), value => console.log(value,2))
promise1.then().then().then( value => console.log(value,3), value => console.log(value,4))

预计输出:

  • 成功 1
  • 失败 4

测试结果

image.png

完整代码

const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "reject"; // 失败
const isFunction = (value) => typeof value === "function";
class MyPromise {
  // Promise 状态
  status = PENDING;
  // 成功的值
  value = undefined;
  // 失败的值
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];
  // 执行器中需要捕获错误
  constructor(callback) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  resolve = (value) => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功的值
    this.value = value;
    // 判断成功回调是否存在,如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
      this.successCallback.shift()();
    }
  };
  reject = (value) => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败的值
    this.reason = value;
    // 判断失败回调是否存在,如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
      this.failCallback.shift()();
    }
  };
  then(successCallback, failCallback) {
    successCallback = isFunction(successCallback)
      ? successCallback
      : (value) => value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) => {
          throw err;
        };
    let thenPromise = new MyPromise((resolve, reject) => {
      // 如果是成功的状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断x是否 MyPromise 类
            // 是 就直接
            resolvePromise(thenPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
        // resolve(x)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(thenPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 等待的情况
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return thenPromise;
  }
}
// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise(thenPromise, value, resolve, reject) {
  if (thenPromise === value) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<MyPromise>")
    );
  }
  if (value instanceof MyPromise) {
    value.then(resolve, reject);
  } else {
    resolve(value);
  }
}
module.exports = MyPromise;

Promiseall方法

all是用来解决异步并发问题的,它允许我们按照异步代码调用的顺序得到异步代码执行的结果

例子

function promise () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功1')
    }, 2000)
  })
}
function promise1 () {
  return new Promise((resolve, reject) => {
    resolve('成功1')
  })
}

这里有两个函数,如果我们同时调用promisepromise1那肯定先得到promise1的代码,之后在获得promise的值,但是Promise.all就可以promisepromise1获得这样的执行顺序。 这是怎么实现的呢?

我们先来看看参数和返回值等

参数

返回值

  • 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved) 状态的 Promise
  • 如果传入的参数不包含任何 promise,则返回一个异步完成(asynchronously resolved)  Promise。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved) 状态的 Promise
  • 其它情况下返回一个处理中(pending)Promise。这个返回的 promise 之后会在所有的 promise 都完成或有一个 promise 失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”示例。返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。

说明

此方法在集合多个 promise 的返回结果时很有用。

完成(Fulfillment):
如果传入的可迭代对象为空,Promise.all 会同步地返回一个已完成(resolved)状态的promise
如果所有传入的 promise 都变为完成状态,或者传入的可迭代对象内没有 promisePromise.all 返回的 promise 异步地变为完成。
在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。

失败/拒绝(Rejection):
如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。

示例

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});

实现

看上面的使用示例,Promise.all, 所以all方法一定是一个静态方法

ok,开整

1. 添加静态方法all

myPromise中添加静态方法,并为他添加形参(类型为数组)

static all (array) {
  
}

2.返回值

我们回顾一下刚刚看的Promise.all的返回值,是不是都是一个Promise对象!!所以!

static all (array) {
    return new MyPromise((resolve, reject) =>{
      
    })
  }

3.判断值的类型

返回值 有提到,我们需要根据值的类型进行返回,那我们就需要判断我们传入的数组是什么值,如果是不是Promise对象,就直接放入结果值的数组中,是Promise对象的话,执行完毕之后再放入结果值的数组中

static all (array) {
    return new MyPromise((resolve, reject) =>{
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          // MyPromise对象
         
        }else {
          // 非MyPromise对象
         
        }
      }
    })
  }

4.存储结果

上面也说了我们需要根据情况进行数据的存储,那么就要按照对应的顺序进行保存结果,万幸数组可以通过key进行值的存储

static all (array) {
    // 存储结果
    let result = [];
    function addData(key, value) {
      // 这里是为了按照执行顺序放入值,保证顺序不会乱!
      result[key] = value
    }
    return new MyPromise((resolve, reject) =>{
      // 这里为什么用for循环而不用
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          // MyPromise对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 非MyPromise对象
          addData(i, current)
        }
      }
      resolve(result)
    })
  }

但是这样还是有问题的!是什么问题呢?

ceeb653ely1gam2j9ym3tg20dc0cmab5.gif 它不支持异步操作欸!卧槽卧槽卧槽!太扎心了

为什么不支持异步操作呢?

别问,问就是不会!开玩笑,for操作是同步,导致异步操作不能及时执行啦,就这么简单

完善代码!

也很简单,我们只需要加一个执行了多少次的变量,然后根据这个变量进行判断,如果他当前的执行次数和我们的入参长度一直,我们就让他执行resolve方法即可

static all (array) {
    // 存储结果
    let result = [];
    let index = [];
    return new MyPromise((resolve, reject) =>{
      function addData(key, value) {
        // 这里是为了按照执行顺序放入值,保证顺序不会乱!
        result[key] = value
        index++;
        if(index === array.length){
          resolve(result)
        }
      }
      // 这里为什么用for循环而不用
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          // MyPromise对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 非MyPromise对象
          addData(i, current)
        }
      }
    })
  }

测试

function promise () {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 2000)
  })
}
function promise1 () {
  return new MyPromise((resolve, reject) => {
    resolve('成功1')
  })
}
MyPromise.all(['a','b',promise(),promise1()]).then((res) => {
  console.log(res)
})

预期输出: [ 'a', 'b', '成功', '成功1' ]

结果

image.png

Promise.resolve 方法的实现

Promise.resolve(value) 方法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。

该怎么实现呢?

首先先理清楚一下需求,Promise.resolve(value) 会判断value是否Promise对象如果是的话就直接返回,不是的话,我们需要包装一个Promise对象,在进行返回,看起来是不是很简单?

上代码

static resolve (value) {
    if(value instanceof MyPromise){
      // MyPromise对象
      return value
    }else {
      // 非MyPromise对象
      return new MyPromise(resolve => resolve(value))
    }
  }

测试

测试用例

function promise () {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 2000)
  })
}
function promise1 () {
  return new MyPromise((resolve, reject) => {
    resolve('成功1')
  })
}
MyPromise.resolve(100).then(res => console.log(res))
MyPromise.resolve(promise()).then(res => console.log(res))
MyPromise.resolve(promise1()).then(res => console.log(res))

预计输出: 100 成功1 成功

测试结果

妥! image.png

finally 方法的实现

finally 方法有两个特点

  1. 不论promise对象状态是否成功 finally的回调函数都会执行
  2. finally 后面可以链式调用then方法获取返回值

如何实现呢?

首先我们可以确定 finally 他不是静态方法,所以我们进入文件中

 finally(callback){
 
 }

然后该怎么做呢??? 根据需求来一点一点实现

不论promise对象状态是否成功 finally的回调函数都会执行

那这就很简单,我们在调用 finally时在函数内部也调用一下then函数,不就好了吗?

还记得then的两个参数吗? 我们同时传入,并调用我们的方法即可!

finally(callback){
    this.then(() =>{
      callback()
    },() =>{
      callback()
    })
  };

完美解决!

finally 后面可以链式调用then方法获取返回值

这个我们就提过很多次了,可以链式调用then那不就是返回了一个promise对象吗? 让我们想想看then中好像也返回了 这个对象那我们何尝不可返回这个then呢?

finally(callback){
   return this.then(() =>{
      callback()
    },() =>{
      callback()
    })
  };

但是这样好像又不太对! 之后的链式调用好像获取不到参数诶怎么办?别慌!稍微在改造一些就好!

finally(callback){
   return this.then(value =>{
      callback()
      return value
    },reason =>{
      callback()
      throw reason
    })
  };

出现了一个问题

什么情况呢?

看看这个测试用代码:

function promise () {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise 成功')
    }, 1000)
  })
}

function promise1 () {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise1 成功')
    }, 1000)
  })
}
promise().finally(() => {
  console.log('promise finally')
  return promise1()
}).then(value => { 
    console.log(value) 
  },reason => { 
    console.log(reason) 
})

我们的期望是先执行promise1这个函数再去执行之后的then,可是并没有这样执行

image.png 该怎么办呢?(动图截取不出来)

正纠结呢,我突然想到,如果我们可以等这个方法执行完毕再往后执行就好啦! 根据我们的执行方法是不是异步在进行处理,转换成promise对象,那不就又可以进行之后的链式调用,还可以执行我们的方法???

resolve``Promise.resolve(value) 方法返回一个以给定值解析后的Promise 对象!!!! 那我们就可以通过它进行代码的修改了!

重新测试

这个测试没问题,不知道为啥我的动图截取不出来,大家将就着看我的描述吧!

测试结果没有问题,等待了1s之后才执行的,没毛病!

catch 方法的实现

catch方法是为了处理Promise方法失败的情况的函数,使用catch就可以不用给then方法传入失败的回调

实现

catch (failCallback) {
    return this.then(undefined, failCallback)
};

就这么简单!

测试一下

测试代码

const MyPromise = require('./myPromise')
function promise () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise 成功')
    }, 1000)
  })
}

function promise1 () {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      reject('promise1 失败')
    }, 1000)
  })
}
promise()
  .then(val => console.log(val))
  .catch(res => console.log(res))

promise1()
  .then(val => console.log(val))
  .catch(res => console.log(res))

结果

image.png

小结

到这里我们的手写代码就结束了

完整代码

const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "reject"; // 失败
const isFunction = (value) => typeof value === "function";
class MyPromise {
  // Promise 状态
  status = PENDING;
  // 成功的值
  value = undefined;
  // 失败的值
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];
  // 执行器中需要捕获错误
  constructor (callback) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  resolve = (value) => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功的值
    this.value = value;
    // 判断成功回调是否存在,如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
      this.successCallback.shift()();
    }
  };
  reject = (value) => {
    // 如果状态不是等待,阻止程序向下执行
    if (this.status !== PENDING) {
      return;
    }
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败的值
    this.reason = value;
    // 判断失败回调是否存在,如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
      this.failCallback.shift()();
    }
  };
  then (successCallback, failCallback) {
    successCallback = isFunction(successCallback)
      ? successCallback
      : (value) => value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) => {
        throw err;
      };
    let thenPromise = new MyPromise((resolve, reject) => {
      // 如果是成功的状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断x是否 MyPromise 类
            // 是 就直接
            resolvePromise(thenPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
        // resolve(x)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(thenPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 等待的情况
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return thenPromise;
  };
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  };
  catch (failCallback) {
    return this.then(undefined, failCallback)
  };
  static all (array) {
    // 存储结果
    let result = [];
    let index = [];
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        // 这里是为了按照执行顺序放入值,保证顺序不会乱!
        result[key] = value
        index++;
        if (index === array.length) {
          resolve(result)
        }
      }
      // 这里为什么用for循环而不用
      for (const i in array) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // MyPromise对象
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 非MyPromise对象
          addData(i, current)
        }
      }
    })
  };
  static resolve (value) {
    if (value instanceof MyPromise) {
      // MyPromise对象
      return value
    } else {
      // 非MyPromise对象
      return new MyPromise(resolve => resolve(value))
    }
  }
}
// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise (thenPromise, value, resolve, reject) {
  if (thenPromise === value) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<MyPromise>")
    );
  }
  if (value instanceof MyPromise) {
    value.then(resolve, reject);
  } else {
    resolve(value);
  }
}
module.exports = MyPromise;

看到这里了,给个赞吧!

be233cf99e9e5e05285d875f4628de81.gif