由浅入深,从掌握Promise的基本使用到手写Promise

前言

在ES6之前,对于一些异步任务的处理始终没有很好的方案可以解决,处理异步的方案可谓是十分混乱,在业务需求下异步请求的套用,就形成了回调地狱,严重影响代码的阅读性。而Promise的出现,给我们统一了规范,解决了之前处理异步任务的许多痛点,并且它友好的使用方式,使之成为了JavaScript一大重点,同时也是面试的高频问点,下面就一起来全面认识一下Promise吧。

1.什么是Promise?

如果我们想在一个异步请求之后,拿到请求的结果,在ES6之前我们可以怎么做呢?

比如,给定一个请求地址,希望拿到它请求成功或者失败的结果:

function request(url, successCb, failCb) {
  setTimeout(function() {
    if (url === '/aaa/bbb') { // 请求成功
      let res = [1, 2, 3]
      successCb(res)
    } else { // 请求失败
      let err = 'err message'
      failCb(err)
    }
  })
}

// 调用方式,从回调中拿结果
request('/aaa/bbb', function(res) {
  console.log(res)
}, function(err) {
  console.log(err)
})

将上面的情况使用Promise来实现一下:

function request(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === '/aaa/bbb') {
        let res = [1, 2, 3]
        resolve(res) // 请求成功调用resolve
      } else {
        let err = 'err message'
        reject(err) // 请求失败调用reject
      }
    })
  })
}

const p = request('/aaa/bbb')

p.then(res => {
  console.log(res) // 拿到resolve传递过来的值
}, err => {
  console.log(err) // 拿到reject传递过来的值
})

2.Promise的三种状态

为什么Promise能够将请求的结果准确的传递到then中的回调函数中,因为Promise其核心就用三种状态来进行管控。

通过上面的案例,可以在浏览器中查看Promise分别在执行resolve和reject后的打印结果和Promise当时处于的状态:

注意:在后续的对Promise的讲述过程中,都需要带着Promise的状态去理解。

3.executor

executor是在创建Promise是需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数,分别就是resolve和reject。

new Promise((resolve, reject) => {
  console.log('我是executor中的代码,我会被立即执行~')
})

通常我们会在executor中确定Promise的状态,而且状态一旦被确定下来,Promise的状态就会被锁死,即Promise的状态一旦修改,就不能再次更改了

4.resolve的参数

上面聊到了resolve需要传入一个普通值,Promise的状态才会被立即锁定为fulfilled,那么如果传递的不是普通值呢?一般resolve传递以下三类值,会有不同的表现效果。

5.Promise相关实例方法

Promise的实例方法,就是可以通过其实例对象进行调用的方法。

5.1.then方法

then方法是Promise实例对象上的一个方法:Promise.prototype.then

(1)then方法接收两个参数

promise.then(res => {
  console.log('状态变成fulfilled回调')
}, err => {
  console.log('状态变成rejected回调')
})

(2)then方法多次调用

const p = new Promise((resolve, reject) => {
  resolve('aaa')
})

p.then(res => {
  console.log(res) // aaa
})
p.then(res => {
  console.log(res) // aaa
})
p.then(res => {
  console.log(res) // aaa
})

(3)then方法中的返回值

then调用本身是有返回值的,并且它的返回值是一个Promise,所以then可以进行链式调用,但是then方法调用的返回值的状态是什么呢?主要是由其返回值决定的。

5.2.catch方法

catch方法是Promise实例对象上的一个方法:Promise.prototype.catch

(1)catch方法可多次调用

const p = new Promise((resolve, reject) => {
  reject('err message')
})

p.catch(err => {
  console.log(err) // err message
})
p.catch(err => {
  console.log(err) // err message
})
p.catch(err => {
  console.log(err) // err message
})

(2)catch方法的返回值

(3)catch的作用

5.3.finally方法

finally方法是Promise实例对象上的一个方法:Promise.prototype.finally

const p = new Promise((resolve, reject) => {
  resolve(123)
})

p.then(res => {
  console.log(res) // 123
}).catch(err => {
  console.log(err)
}).finally(() => {
  console.log('finally code') // finally code
})

6.Promise相关类方法

Promise的类方法,就是直接通过Promise进行调用。

6.1.resolve方法

resolve方法具体有什么用呢?当我们希望将一个值转成Promise来使用,就可以通过直接调用resolve方法来实现,其效果就相当于在new一个Promise时在executor中执行了resolve方法。

resolve传入的参数类型:

6.2.reject方法

reject方法和resolve的用法一致,只不过是将可以得到一个状态为rejected的Promise对象,并且reject不过传入的是什么参数,都会原封不动作为rejected状态传递到catch中。

// 1.传入普通值
const p1 = Promise.reject(123)
p1.then(res => {
  console.log(res)
}).catch(err => {
  console.log('err:', err)
})

// 2.传入实现then方法对象
const p2 = Promise.reject({
  then: function(resolve, reject) {
    resolve('aaaa')
  }
})
p2.then(res => {
  console.log(res)
}).catch(err => {
  console.log('err:', err)
})

// 3.传入Promise
const p3 = Promise.reject(new Promise((resolve, reject) => {
  resolve('aaaa')
 })
)
p3.then(res => {
  console.log(res)
}).catch(err => {
  console.log('err:', err)
})
由浅入深,从掌握Promise的基本使用到手写Promise

6.3.all方法

all方法可以接收由多个Promise对象组成的数组(准确来说是可接收一个可迭代对象),all方法调用返回的Promise状态,由所有Promise对象共同决定。

6.4.allSettled方法

相比于all方法,allSettled方法不管传入的Promise对象的状态是fulfilled还是rejected,最终都会讲结果返回,并且返回的结果是一个数组,数组中存放着每一个Promise对应的状态status和对应的值value。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err message')
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(333)
  }, 3000)
})

Promise.allSettled([p1, p2, p3]).then(res => {
  console.log('res:', res)
}).catch(err => {
  console.log('err:', err)
})
由浅入深,从掌握Promise的基本使用到手写Promise

6.5.race方法

race翻译为竞争,顾名思义哪一个Promise对象最先返回结果,就使用最先返回结果的Promise状态。

一下代码是p1最先有结果的,p1中执行的是resolve,所以返回的状态为fulfilled:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err message')
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(333)
  }, 3000)
})

Promise.race([p1, p2, p3]).then(res => {
  console.log('res:', res) // res: 111
}).catch(err => {
  console.log('err:', err)
})

6.6.any方法

any方法是ES12中新增的方法,与race是类似的,any方法会等到有一个fulfilled状态的Promise,才会决定any调用返回新Promise的状态(也就是说any一定会等到有一个Promise状态为fullfilled)。

那么,如果所有的Promise对象的状态都变为了rejected呢?最终就会报一个AggregateError错误,如果想拿到所有的rejected状态的返回值,可以通过在捕获异常回调参数中的errors获取:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err message1')
  }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err message2')
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('err message3')
  }, 3000)
})

Promise.any([p1, p2, p3]).then(res => {
  console.log('res:', res)
}).catch(err => {
  console.log(err)
  console.log(err.errors)
})
由浅入深,从掌握Promise的基本使用到手写Promise

注意:any方法是ES12新增的,node版本过低的话是会报错找不到any方法的,可以在浏览器中测试。

7.手写Promise

掌握了以上Promise的用法,那么就一步步来实现一下Promise吧。

7.1.executor的实现

// 定义Promise的三种状态常量
const PENDING_STATUS = 'pending'
const FULFILLED_STATUS = 'fulfilled'
const REJECTED_STATUS = 'rejected'

class MyPromise {
  constructor(executor) {
    // 初始化Promise的状态为pending
    this.promiseStatus = PENDING_STATUS
    // 初始化变量,用于保存resolve和reject传入的参数值
    this.value = undefined
    this.reason = undefined

    // 1.定义executor需要传入的resolve函数
    const resolve = (value) => {
      // 只有当Promise的状态为pending,才能将状态改变fulfilled
      if (this.promiseStatus === PENDING_STATUS) {
        this.promiseStatus = FULFILLED_STATUS
        this.value = value
        console.log('调用了resolve,状态变成fulfilled啦~')
      }
    }

    // 2.定义executor需要传入的reject函数
    const reject = (reason) => {
      // 只有当Promise的状态为pending,才能将状态改变为rejected
      if (this.promiseStatus === PENDING_STATUS) {
        this.promiseStatus = REJECTED_STATUS
        this.reason = reason
        console.log('调用了reject,状态变成rejected啦~')
      }
    }

    // 3.将定义的两个函数传入executor并调用
    executor(resolve, reject)
  }
}

简单测试一下:

// 先调用resolve
new MyPromise((resolve, reject) => {
  resolve()
  reject()
})
// 先调用reject
new MyPromise((resolve, reject) => {
  reject()
  resolve()
})
由浅入深,从掌握Promise的基本使用到手写Promise

7.2.then方法的实现

(1)then基本实现

class MyPromise {
  constructor(executor) {
    // 初始化Promise的状态为pending
    this.promiseStatus = PENDING_STATUS
    // 初始化变量,用于保存resolve和reject传入的参数值
    this.value = undefined
    this.reason = undefined

    // 1.定义executor需要传入的resolve函数
    const resolve = (value) => {
      // 只有当Promise的状态为pending,才能将状态改变fulfilled
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = FULFILLED_STATUS
          this.value = value
          // 状态变成fulfilled就去调用onFulfilled
          this.onFulfilled(this.value)
        })
      }
    }

    // 2.定义executor需要传入的reject函数
    const reject = (reason) => {
      // 只有当Promise的状态为pending,才能将状态改变为rejected
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = REJECTED_STATUS
          this.reason = reason
          // 状态变成rejected就去调用onRejected
          this.onRejected(this.reason)
        })
      }
    }

    // 3.将定义的两个函数传入executor并调用
    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    // 保存fulfilled和rejected状态的回调
    this.onFulfilled = onFulfilled
    this.onRejected = onRejected
  }
}

注意:这里将onFulfilled和onRejected的调动放在了queueMicrotask,在JavaScript中可以通过queueMicrotask使用微任务,而原Promise的then中回调的执行,也是会被放在微任务中的,为什么要放在微任务中呢?

原因:如果不使用微任务,那么在executor中执行resolve或者reject时,then方法还没被调用,onFulfilled和onRejected就都还没被赋值,所以调用时会报错,加入微任务就可以实现将onFulfilled和onRejected的调用推迟到下一次事件循环,也就是等then调用后赋值了才会执行。

简单测试一下:

const p1 = new MyPromise((resolve, reject) => {
  resolve('aaaa')
})
const p2 = new MyPromise((resolve, reject) => {
  reject('err message')
})

p1.then(res => {
  console.log(res) // aaaa
}, err => {
  console.log(err)
})

p2.then(res => {
  console.log(res)
}, err => {
  console.log(err) // err message
})

(2)then优化一

class MyPromise {
  constructor(executor) {
    // 初始化Promise的状态为pending
    this.promiseStatus = PENDING_STATUS
    // 初始化变量,用于保存resolve和reject传入的参数值
    this.value = undefined
    this.reason = undefined
    // 初始化两个数组,分别用于保存then中对应需要执行的回调
    this.onFulfilledFns = []
    this.onRejectedFns = []

    // 1.定义executor需要传入的resolve函数
    const resolve = (value) => {
      // 只有当Promise的状态为pending,才能将状态改变fulfilled
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = FULFILLED_STATUS
          this.value = value
          // 状态变成fulfilled就去遍历调用onFulfilled
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        })
      }
    }

    // 2.定义executor需要传入的reject函数
    const reject = (reason) => {
      // 只有当Promise的状态为pending,才能将状态改变为rejected
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = REJECTED_STATUS
          this.reason = reason
          // 状态变成rejected就去遍历调用onRejected
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }

    // 3.将定义的两个函数传入executor并调用
    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
    if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) {
      onFulfilled(this.value)
    }
    if (this.promiseStatus === REJECTED_STATUS && onRejected) {
      onRejected(this.reason)
    }

    // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
    if (this.promiseStatus === PENDING_STATUS) {
      // 将then中成功的回调和失败的回调分别存入数组中
      this.onFulfilledFns.push(onFulfilled)
      this.onRejectedFns.push(onRejected)
    }
  }
}

简单测试一下:

const p = new MyPromise((resolve, reject) => {
  resolve('aaaa')
  reject('err message')
})

p.then(res => {
  console.log('res1:', res)
}, err => {
  console.log('err1:', err)
})

p.then(res => {
  console.log('res2:', res)
}, err => {
  console.log('err1:', err)
})

setTimeout(() => {
  p.then(res => {
    console.log('res3:', res)
  }, err => {
    console.log('err1:', err)
  })
})
由浅入深,从掌握Promise的基本使用到手写Promise

(3)then优化二

class MyPromise {
  constructor(executor) {
    // 初始化Promise的状态为pending
    this.promiseStatus = PENDING_STATUS
    // 初始化变量,用于保存resolve和reject传入的参数值
    this.value = undefined
    this.reason = undefined
    // 初始化两个数组,分别用于保存then中对应需要执行的回调
    this.onFulfilledFns = []
    this.onRejectedFns = []

    // 1.定义executor需要传入的resolve函数
    const resolve = (value) => {
      // 只有当Promise的状态为pending,才能将状态改变fulfilled
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = FULFILLED_STATUS
          this.value = value
          // 状态变成fulfilled就去遍历调用onFulfilled
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        })
      }
    }

    // 2.定义executor需要传入的reject函数
    const reject = (reason) => {
      // 只有当Promise的状态为pending,才能将状态改变为rejected
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = REJECTED_STATUS
          this.reason = reason
          // 状态变成rejected就去遍历调用onRejected
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }

    // 3.将定义的两个函数传入executor并调用
    // 如果executor中就抛出了异常,那么直接执行reject即可
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
      if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) {
        // 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject
        try {
          const value = onFulfilled(this.value)
          resolve(value)
        } catch (err) {
          reject(err)
        }
      }
      if (this.promiseStatus === REJECTED_STATUS && onRejected) {
        try {
          const reason = onRejected(this.reason)
          resolve(reason)
        } catch(err) {
          reject(err)
        }
      }

      // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
      if (this.promiseStatus === PENDING_STATUS) {
        // 将then中成功的回调和失败的回调分别存入数组中
        // 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果
        this.onFulfilledFns.push(() => {
          try {
            const value = onFulfilled(this.value)
            resolve(value)
          } catch(err) {
            reject(err)
          }
        })
        this.onRejectedFns.push(() => {
          try {
            const reason = onRejected(this.reason)
            resolve(reason)
          } catch(err) {
            reject(err)
          }
        })
      }
    })
  }
}

简单测试一下:

const p = new MyPromise((resolve, reject) => {
  resolve('aaaa')
})

p.then(res => {
  console.log('res1:', res)
  return 'bbbb'
}, err => {
  console.log('err1:', err)
}).then(res => {
  console.log('res2:', res)
  throw new Error('err message')
}, err => {
  console.log('err2:', err)
}).then(res => {
  console.log('res3:', res)
}, err => {
  console.log('err3:', err)
})
由浅入深,从掌握Promise的基本使用到手写Promise

7.3.catch方法的实现

// catch方法实现
catch(onRejected) {
  return this.then(undefined, onRejected)
}

then方法改进:

由浅入深,从掌握Promise的基本使用到手写Promise

简单测试一下:

const p = new MyPromise((resolve, reject) => {
  reject('err message')
})

p.then(res => {
  console.log(res)
}).catch(err => {
  console.log(err) // err message
})

7.4.finally方法的实现

finally(onFinally) {
  this.then(() => {
    onFinally()
  }, () => {
    onFinally()
  })
  // 也可直接简写成:
  // this.then(onFinally, onFinally)
}

then方法改进:

由浅入深,从掌握Promise的基本使用到手写Promise

简单测试一下:

const p = new MyPromise((resolve, reject) => {
  resolve('aaaa')
})

p.then(res => {
  console.log('res:', res) // res: aaaa
}).catch(err => {
  console.log('err:', err)
}).finally(() => {
  console.log('我是一定会执行的!') // 我是一定会执行的!
})

7.5.resolve和reject方法的实现

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

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

简单测试一下:

MyPromise.resolve('aaaa').then(res => {
  console.log(res) // aaaa
})
MyPromise.reject('bbbb').then(res => {
  console.log(res)
}, err => {
  console.log(err) // bbbb
})

7.6.all方法的实现

static all(promises) {
  return new MyPromise((resolve, reject) => {
    // 用于存放所有成功的返回值
    const results = []
    promises.forEach(promise => {
      promise.then(res => {
        results.push(res)

        // 当成功返回值的长度与传入promises的长度相等,就调用resolve
        if (results.length === promises.length) {
          resolve(results)
        }
      }, err => {
        // 一旦有一个promise变成了rejected状态,就调用reject
        reject(err)
      })
    })
  })
}

简单测试一下:

const p1 = new MyPromise(resolve => {
  setTimeout(() => {
    resolve('aaaa')
  }, 1000)
})
const p2 = new MyPromise(resolve => {
  setTimeout(() => {
    resolve('bbbb')
  }, 2000)
})
const p3 = new MyPromise(resolve => {
  setTimeout(() => {
    resolve('cccc')
  }, 3000)
})

MyPromise.all([p1, p2, p3]).then(res => {
  console.log(res) // [ 'aaaa', 'bbbb', 'cccc' ]
}).catch(err => {
  console.log(err)
})

7.7.allSettled方法的实现

static allSettled(promises) {
  return new MyPromise((resolve, reject) => {
    // 用于存放所有promise的状态和返回值
    const results = []
    promises.forEach(promise => {
      promise.then(res => {
        results.push({ status: FULFILLED_STATUS, value: res })
        // 当长度相等,调用resolve
        if (results.length === promises.length) {
          resolve(results)
        }
      }, err => {
        results.push({ status: REJECTED_STATUS, value: err })
        // 当长度相等,调用resolve
        if (results.length === promises.length) {
          resolve(results)
        }
      })
    })
  })
}

简单测试一下:

const p1 = new MyPromise(resolve => {
  setTimeout(() => {
    resolve('aaaa')
  }, 1000)
})
const p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('err message')
  }, 2000)
})
const p3 = new MyPromise(resolve => {
  setTimeout(() => {
    resolve('bbbb')
  }, 3000)
})

MyPromise.allSettled([p1, p2, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})
由浅入深,从掌握Promise的基本使用到手写Promise

7.8.race方法的实现

static race(promises) {
  return new MyPromise((resolve, reject) => {
    promises.forEach(promise => {
      // 得到状态最先改变的promise,调用对应的resolve和reject
      promise.then(res => {
        resolve(resolve)
      }, err => {
        reject(err)
      })
    })
  })
}

7.9.any方法的实现

static any(promises) {
  return new MyPromise((resolve, reject) => {
    // 用于记录状态为rejected的值
    const reasons = []
    promises.forEach(promise => {
      promise.then(res => {
        // 当有一个promise变成fulfilled状态就调用resolve
        resolve(res)
      }, err => {
        reasons.push(err)
        // 当所有promise都是rejected就调用reject,并且传入AggregateError
        if (reasons.length === promises.length) {
          reject(new AggregateError(reasons))
        }
      })
    })
  })
}

简单测试一下:

const p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('err message1')
  }, 1000)
})
const p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('err message2')
  }, 2000)
})
const p3 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('err message3')
  }, 3000)
})

MyPromise.any([p1, p2, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log('err:', err)
  console.log(err.errors)
})
由浅入深,从掌握Promise的基本使用到手写Promise

7.10.Promise手写完整版整理

上面已经对Promise的各个功能进行了实现,下面就来整理一下最终的完整版,可以将一些重复的逻辑抽取出去,比如try catch。

// 定义Promise的三种状态常量
const PENDING_STATUS = 'pending'
const FULFILLED_STATUS = 'fulfilled'
const REJECTED_STATUS = 'rejected'
// try catch逻辑抽取
function tryCatchFn(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}
class MyPromise {
  constructor(executor) {
    // 初始化Promise的状态为pending
    this.promiseStatus = PENDING_STATUS
    // 初始化变量,用于保存resolve和reject传入的参数值
    this.value = undefined
    this.reason = undefined
    // 初始化两个数组,分别用于保存then中对应需要执行的回调
    this.onFulfilledFns = []
    this.onRejectedFns = []

    // 1.定义executor需要传入的resolve函数
    const resolve = (value) => {
      // 只有当Promise的状态为pending,才能将状态改变fulfilled
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = FULFILLED_STATUS
          this.value = value
          // 状态变成fulfilled就去遍历调用onFulfilled
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        })
      }
    }

    // 2.定义executor需要传入的reject函数
    const reject = (reason) => {
      // 只有当Promise的状态为pending,才能将状态改变为rejected
      if (this.promiseStatus === PENDING_STATUS) {
        // 添加微任务
        queueMicrotask(() => {
          // 如果Promise状态不为pending,后面的代码就不再执行了
          if (this.promiseStatus !== PENDING_STATUS) return
          this.promiseStatus = REJECTED_STATUS
          this.reason = reason
          // 状态变成rejected就去遍历调用onRejected
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }

    // 3.将定义的两个函数传入executor并调用
    // 如果executor中就抛出了异常,那么直接执行reject即可
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    // 判断onRejected是否有值,没有值的话直接赋值一个抛出异常的方法,用于传递到下一次then中的失败回调,供catch调用
    onRejected = onRejected || (err => {throw err})

    // 判断onFulfilled是否有值,避免在使用catch时传入的undefined不会执行,出现断层现象
    onFulfilled = onFulfilled || (value => value)

    return new MyPromise((resolve, reject) => {
      // 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
      if (this.promiseStatus === FULFILLED_STATUS) {
        // 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject
        tryCatchFn(onFulfilled, this.value, resolve, reject)
      }
      if (this.promiseStatus === REJECTED_STATUS) {
        tryCatchFn(onRejected, this.reason, resolve, reject)
      }

      // 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
      if (this.promiseStatus === PENDING_STATUS) {
        // 将then中成功的回调和失败的回调分别存入数组中
        // 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果
        this.onFulfilledFns.push(() => {
          tryCatchFn(onFulfilled, this.value, resolve, reject)
        })
        this.onRejectedFns.push(() => {
          tryCatchFn(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally) {
    this.then(onFinally, onFinally)
  }

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

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

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 用于存放所有成功的返回值
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push(res)

          // 当成功返回值的长度与传入promises的长度相等,就调用resolve
          if (results.length === promises.length) {
            resolve(results)
          }
        }, err => {
          // 一旦有一个promise变成了rejected状态,就调用reject
          reject(err)
        })
      })
    })
  }

  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      // 用于存放所有promise的状态和返回值
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push({ status: FULFILLED_STATUS, value: res })
          // 当长度相等,调用resolve
          if (results.length === promises.length) {
            resolve(results)
          }
        }, err => {
          results.push({ status: REJECTED_STATUS, value: err })
          // 当长度相等,调用resolve
          if (results.length === promises.length) {
            resolve(results)
          }
        })
      })
    })
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        // 得到状态最先改变的promise,调用对应的resolve和reject
        promise.then(resolve, reject)
      })
    })
  }

  static any(promises) {
    return new MyPromise((resolve, reject) => {
      // 用于记录状态为rejected的值
      const reasons = []
      promises.forEach(promise => {
        promise.then(res => {
          // 当有一个promise变成fulfilled状态就调用resolve
          resolve(res)
        }, err => {
          reasons.push(err)
          // 当所有promise都是rejected就调用reject,并且传入AggregateError
          if (reasons.length === promises.length) {
            reject(new AggregateError(reasons))
          }
        })
      })
    })
  }
}


本文作者MomentYY
本文链接https://www.cnblogs.com/MomentYY/p/16096666.html

展开阅读全文

页面更新:2024-05-15

标签:都会   由浅入深   数组   初始化   函数   异常   定义   对象   状态   参数   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top