title: Promise使用
categories: Javascript
tag:

  • promise
    date: 2021-11-29 21:16:34

Promise 使用详解

我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟);

  • 如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去;
  • 如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息;

22_Promise使用 - 图1

在以前,我们使用的是传入回调函数。

22_Promise使用 - 图2

在上面的解决方案中,我们确确实实可以解决请求函数得到结果之后,获取到对应的回调,但是它存在两个主要的

问题:

  1. 第一,我们需要自己来设计回调函数、回调函数的名称、回调函数的使用等;
  2. 第二,对于不同的人、不同的框架设计出来的方案是不同的,那么我们必须耐心去看别人的源码或者文档,以便可以理解它这个函数到底怎么用;

什么是 Promise?

我们来看一下 Promise 的 API 是怎么样的:

  • Promise 是一个类,可以翻译成 承诺、许诺 、期约;
  • 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个 Promise 的对象;
  • 在通过 new 创建 Promise 对象时,我们需要传入一个回调函数,我们称之为 executor
    1. 这个回调函数会被立即执行,并且给传入另外两个回调函数 resolve、reject;
    2. 当我们调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调函数;
    3. 当我们调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调函数;
  1. //Promise是一个构造函数
  2. // > resolve: 成功时调用的回调函数
  3. // > reject: 失败时调用的回调函数
  4. // then方法传入的函数会在Promise执行resolve函数的时候回调
  5. // catch方法传入的函数会在Promise执行reject函数的时候回调
  6. const promise = new Promise((resolve, reject) => {
  7. console.log('promise执行了')
  8. resolve()
  9. })
  10. .then()
  11. .catch()

重构导语中的网络请求

  1. function requestData(url, success, failure) {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. //拿到请求的结果
  5. if (url == 'why') {
  6. //成功
  7. let names = ['abc', 'cba']
  8. resolve(names)
  9. } else {
  10. //失败
  11. let errMessage = '请求失败了'
  12. reject(errMessage)
  13. }
  14. }, 2000)
  15. })
  16. }
  17. const promise = requestData('why')
  18. //then 可以传递两个回调函数。也可以分开传
  19. // 第一个回调方法传入的函数会在Promise执行resolve函数的时候回调
  20. // 第二个回调传入的函数会在Promise执行reject函数的时候回调
  21. promise.then(
  22. (res) => {
  23. console.log(res)
  24. },
  25. (err) => {
  26. console.log(err)
  27. }
  28. )

Executor

Executor 是在创建 Promise 时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数:

22_Promise使用 - 图3

通常我们会在 Executor 中确定我们的 Promise 状态:

  • 通过 resolve,可以兑现(fulfilled)Promise 的状态,我们也可以称之为已决议(resolved);
  • 通过 reject,可以拒绝(reject)Promise 的状态;

这里需要注意:一旦状态被确定下来,Promise 的状态会被 锁死,该 Promise 的状态是不可更改的 (也就是 resolve()和 reject()只会生效一个)

  • 在我们调用 resolve 的时候,如果 resolve 传入的值本身不是一个 Promise,那么会将该 Promise 的状态变成兑现(fulfilled);
  • 在之后我们去调用 reject 时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变 Promise 状态);
  1. new Promise((resolve, reject) => {
  2. console.log('-------')
  3. resolve() //处于fulfilled状态(已经敲定)
  4. // reject() //处于reject状态(已拒绝状态)
  5. }).then(
  6. (res) => {
  7. console.log(res)
  8. },
  9. (err) => {
  10. console.log(err)
  11. }
  12. )

resolve 参数

resolve 不同值的区别

  1. 情况一:如果 resolve 传入一个普通的值或者对象,那么这个值会作为 then 回调的参数;
    22_Promise使用 - 图4
  2. 情况二:如果 resolve 中传入的是另外一个 Promise,那么这个新 Promise 会决定原 Promise 的状态:
    22_Promise使用 - 图5
  3. 情况三:如果 resolve 中传入的是一个对象,并且这个对象有实现 then 方法,那么会执行该 then 方法,并且根据 then 方法的结果来决定 Promise 的状态:
    22_Promise使用 - 图6

Promise 实例方法

then 方法

then 方法是 Promise 对象上的一个方法:它其实是放在 Promise 的原型上的 Promise.prototype.then

then 方法接受两个参数:

  1. fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
  2. reject 的回调函数:当状态变成 reject 时会回调的函数;

一个 Promise 的 then 方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的 fulfilled 回调;
  • 当 Promise 的状态变成 fulfilled 的时候,这些回调函数都会被执行;

一个 Promise 的 then 方法是可以被多次调用的:案例

22_Promise使用 - 图7

then 的返回值

then 方法本身是有返回值的,它的返回值是一个 Promise,所以我们可以进行如下的链式调用:

  • 但是 then 方法返回的 Promise 到底处于什么样的状态呢?

Promise 有三种状态,那么这个 Promise 处于什么状态呢?

  • 当 then 方法中的回调函数本身在执行的时候,那么它处于 pending 状态;
  • 当 then 方法中的回调函数返回一个结果时,那么它处于 fulfilled 状态,并且会将结果作为 resolve 的参数;
    1. 情况一:返回一个普通的值;
    2. 情况二:返回一个 Promise;
    3. 情况三:返回一个 thenable 值;
  • 当 then 方法抛出一个异常时,那么它处于 reject 状态;
  1. 如果返回的是普通值。内部会产生 promise,并且以返回值 resolve 出来

22_Promise使用 - 图8

  1. 如果返回的是 Promise 对象

22_Promise使用 - 图9

  1. 如果返回的是对象,并且有 then 方法

22_Promise使用 - 图10

catch 方法

catch 方法也是 Promise 对象上的一个方法:它也是放在 Promise 的原型上的 Promise.prototype.catch

一个 Promise 的 catch 方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的 reject 回调;
  • 当 Promise 的状态变成 reject 的时候,这些回调函数都会被执行;
  1. const promise = new Promise((resolve, reject) => {
  2. throw new Error('rejected Status')
  3. })
  4. //也可以捕获throw new Error
  5. promise.catch((err) => {
  6. console.log(err)
  7. })

注意的是

22_Promise使用 - 图11

但是当 then 返回的是 promise

22_Promise使用 - 图12

catch 返回值

事实上 catch 方法也是会返回一个 Promise 对象的,所以 catch 方法后面我们可以继续调用 then 方法或者 catch 方法:

下面的代码,后续是 catch 中的 err2 打印,还是 then 中的 res 打印呢?

22_Promise使用 - 图13

答案是 res 打印,这是因为 catch 传入的回调在执行完后,默认状态依然会是 fulfilled 的;

那么如果我们希望后续继续执行 catch,那么需要抛出一个异常;

finally

finally 是在 ES9(ES2018)中新增的一个特性:表示无论 Promise 对象无论变成 fulfilled 还是 reject 状态,最终都会被执行的代码。

finally 方法是不接收参数的,因为无论前面是 fulfilled 状态,还是 reject 状态,它都会执行。

  1. const promise = new Promise((resolve, reject) => {
  2. reject('111')
  3. })
  4. promise
  5. .then((res) => {
  6. console.log(res)
  7. })
  8. .catch((err) => {
  9. console.log(err)
  10. })
  11. //必会执行finally
  12. .finally(() => {
  13. console.log('finally')
  14. })

Promise 类方法

resolve 方法

前面我们学习的 then、catch、finally 方法都属于 Promise 的实例方法,都是存放在 Promise 的 prototype 上的。

有时候我们已经有一个现成的内容了,希望将其转成 Promise 来使用,这个时候我们可以使用 Promise.resolve 方法来完成。

  • Promise.resolve 的用法相当于 new Promise,并且执行 resolve 操作:
  1. Promise.resolve('why')
  2. // 相当于
  3. new Promise((resolve, reject) => resolve('why'))

resolve 参数的形态:

  1. 情况一:参数是一个普通的值或者对象
  2. 情况二:参数本身是 Promise
  3. 情况三:参数是一个 thenable

reject 方法

reject 方法类似于 resolve 方法,只是会将 Promise 对象的状态设置为 reject 状态。

Promise.reject 的用法相当于 new Promise,只是会调用 reject:

  1. Promise.reject('why')
  2. // 相当于
  3. new Promise((resolve, reject) => reject('why'))

Promise.reject 传入的参数无论是什么形态,都会直接作为 reject 状态的参数传递到 catch 的。

all 方法

另外一个类方法是 Promise.all:

  • 它的作用是将多个 Promise 包裹在一起形成一个新的 Promise;
  • 新的 Promise 状态由包裹的所有 Promise 共同决定:
    1. 当所有的 Promise 状态变成 fulfilled 状态时,新的 Promise 状态为 fulfilled,并且会将所有 Promise 的返回值 组成一个数组;
    2. 当有一个 Promise 状态为 reject 时,新的 Promise 状态为 reject,并且会将第一个 reject 的返回值作为参数;
  1. const promise1 = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve(111)
  4. }, 4000)
  5. })
  6. const promise2 = new Promise((resolve, reject) => {
  7. setTimeout(() => {
  8. resolve(222)
  9. }, 2000)
  10. })
  11. const promise3 = new Promise((resolve, reject) => {
  12. setTimeout(() => {
  13. resolve(333)
  14. }, 3000)
  15. })
  16. Promise.all([promise1, promise2, promise3]).then((res) => {
  17. console.log(res) //[ 111, 222, 333 ]
  18. })

22_Promise使用 - 图14

当有一个 Promise 状态为 reject 时,新的 Promise 状态为 reject,并且会将第一个 reject 的返回值作为参数;

22_Promise使用 - 图15

allSettled 方法

all 方法有一个缺陷:当有其中一个 Promise 变成 reject 状态时,新 Promise 就会立即变成对应的 reject 状态。

  • 那么对于 resolved 的,以及依然处于 pending 状态的 Promise,我们是获取不到对应的结果的;

在 ES11(ES2020)中,添加了新的 API Promise.allSettled:

  • 该方法会在所有的 Promise 都有结果(settled),无论是 fulfilled,还是 reject 时,才会有最终的状态;
  • 并且这个 Promise 的结果一定是 fulfilled 的;

22_Promise使用 - 图16

race 方法

如果有一个 Promise 有了结果,我们就希望决定最终新 Promise 的状态,那么可以使用 race 方法:

  • race 是竞技、竞赛的意思,表示多个 Promise 相互竞争,谁先有结果,那么就使用谁的结果;

如果最先的结果是拒绝,也不会继续执行了

  1. const promise1 = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve(111)
  4. }, 1000)
  5. })
  6. const promise2 = new Promise((resolve, reject) => {
  7. setTimeout(() => {
  8. reject('我拒绝')
  9. }, 2000)
  10. })
  11. const promise3 = new Promise((resolve, reject) => {
  12. setTimeout(() => {
  13. resolve(333)
  14. }, 3000)
  15. })
  16. Promise.race([promise1, promise2, promise3])
  17. .then((res) => {
  18. console.log(res)
  19. })
  20. .catch((err) => {
  21. console.log(err)
  22. })

22_Promise使用 - 图17

any 方法

如果我们非要想等一个 resolve 的结果呢。n

any 方法是 ES12 中新增的方法,和 race 方法是类似的:

any 方法会等到一个 fulfilled 状态,才会决定新 Promise 的状态;

如果所有的 Promise 都是 reject 的,那么也会等到所有的 Promise 都变成 rejected 状态;

  1. const promise1 = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve(111)
  4. }, 4000)
  5. })
  6. const promise2 = new Promise((resolve, reject) => {
  7. setTimeout(() => {
  8. reject('我拒绝')
  9. }, 2000)
  10. })
  11. const promise3 = new Promise((resolve, reject) => {
  12. setTimeout(() => {
  13. resolve(333)
  14. }, 3000)
  15. })
  16. Promise.any([promise1, promise2, promise3])
  17. .then((res) => {
  18. console.log(res)
  19. })
  20. .catch((err) => {
  21. console.log(err)
  22. })
  23. //返回结果是3

如果所有的都是 reject,运行结果为:

  1. AggregateError: All promises were rejected

手写 Promise

按照 promiseA+规范

Promise 状态设计

首先,关于 Promise 状态的设计

  1. const PROMISE_STATUS_FULFILLED = 'fulfilled'
  2. const PROMISE_STATUS_PENDING = 'pending'
  3. const PROMISE_STATUS_REJECTED = 'rejected'
  4. class HyPromise {
  5. constructor(executor) {
  6. this.status = PROMISE_STATUS_PENDING
  7. const resolve = () => {
  8. if (this.status == PROMISE_STATUS_PENDING) {
  9. this.status = PROMISE_STATUS_FULFILLED
  10. console.log('resolve被调用')
  11. }
  12. }
  13. const reject = () => {
  14. if (this.status == PROMISE_STATUS_PENDING) {
  15. this.status = PROMISE_STATUS_REJECTED
  16. console.log('reject被调用')
  17. }
  18. }
  19. executor(resolve, reject)
  20. }
  21. }
  22. const promise = new HyPromise((resolve, reject) => {
  23. resolve()
  24. reject()
  25. })

传递参数

然后,我们需要给 resolve,reject 传递参数

所以我们需要有 value 和 reason 参数

22_Promise使用 - 图18

then/catch 实现

我们在 then 里面添加了onFulfilled, onRejected。把外界调用 then 传入的函数作为onFulfilledonRejected

但是根据执行顺序,这个 then 中的onFulfilledonRejected还没有获取值,我们使用一个 queueMicrotask 把调用onFulfilledonRejected函数变为宏任务。

22_Promise使用 - 图19

then 多次调用

以上代码写好了之后,我们并不能多次调用 then .会覆盖掉

22_Promise使用 - 图20

我们就需要设置两个数组分别保存成功的回调和失败的回调

22_Promise使用 - 图21

22_Promise使用 - 图22

22_Promise使用 - 图23

此时的运行结果

22_Promise使用 - 图24

就可以实现多次调用 then 了

如果我们有一个定时器。过了一秒再调用。可是我们写的 promise 不能调用 then

22_Promise使用 - 图25

那么,我们可以修改 then 方法

22_Promise使用 - 图26

这时候,会发现。会同时执行 resolve 和 reject,那么我们就应该,修改如下代码

22_Promise使用 - 图27

then 链式调用

我们知道 then 的返回值是一个新的 promise。那么也可以链式调用 then。

我们知道不管 then 是 resolve 还是 reject。都会调用 resolve

22_Promise使用 - 图28

那么我们 then 方法就可以修改为

22_Promise使用 - 图29

最后我们的 HyPromise 代码

  1. class HyPromise {
  2. constructor(executor) {
  3. this.status = PROMISE_STATUS_PENDING
  4. this.value = undefined
  5. this.reason = undefined
  6. this.onFulfilledFns = []
  7. this.onRejectedFns = []
  8. const resolve = (value) => {
  9. if (this.status === PROMISE_STATUS_PENDING) {
  10. queueMicrotask(() => {
  11. if (this.status != PROMISE_STATUS_PENDING) return
  12. this.status = PROMISE_STATUS_FULFILLED
  13. this.value = value
  14. this.onFulfilledFns.forEach((fn) => {
  15. fn(this.value)
  16. })
  17. })
  18. }
  19. }
  20. const reject = (reason) => {
  21. if (this.status === PROMISE_STATUS_PENDING) {
  22. queueMicrotask(() => {
  23. if (this.status != PROMISE_STATUS_PENDING) return
  24. this.status = PROMISE_STATUS_REJECTED
  25. this.reason = reason
  26. this.onRejectedFns.forEach((fn) => {
  27. fn(this.reason)
  28. })
  29. })
  30. }
  31. }
  32. try {
  33. executor(resolve, reject)
  34. } catch (err) {
  35. reject(err)
  36. }
  37. }
  38. then(onFulfilled, onRejected) {
  39. return new HyPromise((resolve, reject) => {
  40. if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
  41. try {
  42. const value = onFulfilled(this.value)
  43. resolve(value)
  44. } catch (err) {
  45. reject(err)
  46. }
  47. }
  48. if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
  49. try {
  50. const reason = onRejected(this.reason)
  51. resolve(reason)
  52. } catch (err) {
  53. reject(err)
  54. }
  55. }
  56. if (this.status === PROMISE_STATUS_PENDING) {
  57. this.onFulfilledFns.push(() => {
  58. try {
  59. const value = onFulfilled(this.value)
  60. resolve(value)
  61. } catch (err) {
  62. reject(err)
  63. }
  64. })
  65. this.onRejectedFns.push(() => {
  66. try {
  67. const reason = onRejected(this.reason)
  68. resolve(reason)
  69. } catch (err) {
  70. reject(err)
  71. }
  72. })
  73. }
  74. })
  75. }
  76. }

then 链式调用优化

我们可以看到有很多的 try catch,那么我们可以封装一个工具函数

22_Promise使用 - 图30

  1. function execFunctionWithCatchError(execFn, value, resolve, reject) {
  2. try {
  3. const result = execFn(value)
  4. resolve(result)
  5. } catch (err) {
  6. reject(err)
  7. }
  8. }

那么我们就可以优化为以下代码

  1. const PROMISE_STATUS_FULFILLED = 'fulfilled'
  2. const PROMISE_STATUS_PENDING = 'pending'
  3. const PROMISE_STATUS_REJECTED = 'rejected'
  4. //工具函数
  5. function execFunctionWithCatchError(execFn, value, resolve, reject) {
  6. try {
  7. const result = execFn(value)
  8. resolve(result)
  9. } catch (err) {
  10. reject(err)
  11. }
  12. }
  13. class HyPromise {
  14. constructor(executor) {
  15. this.status = PROMISE_STATUS_PENDING
  16. this.value = undefined
  17. this.reason = undefined
  18. this.onFulfilledFns = []
  19. this.onRejectedFns = []
  20. const resolve = (value) => {
  21. if (this.status === PROMISE_STATUS_PENDING) {
  22. queueMicrotask(() => {
  23. if (this.status != PROMISE_STATUS_PENDING) return
  24. this.status = PROMISE_STATUS_FULFILLED
  25. this.value = value
  26. this.onFulfilledFns.forEach((fn) => {
  27. fn(this.value)
  28. })
  29. })
  30. }
  31. }
  32. const reject = (reason) => {
  33. if (this.status === PROMISE_STATUS_PENDING) {
  34. queueMicrotask(() => {
  35. if (this.status != PROMISE_STATUS_PENDING) return
  36. this.status = PROMISE_STATUS_REJECTED
  37. this.reason = reason
  38. this.onRejectedFns.forEach((fn) => {
  39. fn(this.reason)
  40. })
  41. })
  42. }
  43. }
  44. try {
  45. executor(resolve, reject)
  46. } catch (err) {
  47. reject(err)
  48. }
  49. }
  50. then(onFulfilled, onRejected) {
  51. return new HyPromise((resolve, reject) => {
  52. if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
  53. // try {
  54. // const value = onFulfilled(this.value)
  55. // resolve(value)
  56. // } catch (err) {
  57. // reject(err)
  58. // }
  59. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  60. }
  61. if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
  62. // try {
  63. // const reason = onRejected(this.reason)
  64. // resolve(reason)
  65. // } catch (err) {
  66. // reject(err)
  67. // }
  68. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  69. }
  70. if (this.status === PROMISE_STATUS_PENDING) {
  71. this.onFulfilledFns.push(() => {
  72. // try {
  73. // const value = onFulfilled(onFulfilled)
  74. // resolve(value)
  75. // } catch (err) {
  76. // reject(err)
  77. // }
  78. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  79. })
  80. this.onRejectedFns.push(() => {
  81. // try {
  82. // const reason = onRejected(this.reason)
  83. // resolve(reason)
  84. // } catch (err) {
  85. // reject(err)
  86. // }
  87. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  88. })
  89. }
  90. })
  91. }
  92. }
  93. //测试代码================================================================
  94. const promise = new HyPromise((resolve, reject) => {
  95. reject('222222222')
  96. })
  97. promise
  98. .then(
  99. (res) => {
  100. console.log('res1:', res)
  101. return 11111
  102. },
  103. (err) => {
  104. console.log('err1:', err)
  105. return 'nnnnnn'
  106. }
  107. )
  108. .then(
  109. (res) => {
  110. console.log('res2:', res)
  111. },
  112. (err) => {
  113. console.log(err)
  114. }
  115. )
  116. // promise.then(
  117. // (res) => {
  118. // console.log('res2:', res)
  119. // },
  120. // (err) => {
  121. // console.log('err2:', err)
  122. // }
  123. // )
  124. // setTimeout(() => {
  125. // promise.then(
  126. // (res) => {
  127. // console.log('res3:', res)
  128. // },
  129. // (err) => {
  130. // console.log('err3:', err)
  131. // }
  132. // )
  133. // }, 1000)

实现 catch 方法

本来我们应该是

.then(res(),error())

22_Promise使用 - 图31

当我们调用 catch 的时候,希望链式使用。

22_Promise使用 - 图32

那么最走 then 方法和 catch 方法为如下代码

  1. then(onFulfilled, onRejected) {
  2. onRejected =
  3. onRejected ||
  4. ((err) => {
  5. throw err
  6. })
  7. return new HyPromise((resolve, reject) => {
  8. if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
  9. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  10. }
  11. if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
  12. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  13. }
  14. if (this.status === PROMISE_STATUS_PENDING) {
  15. if (onFulfilled) {
  16. this.onFulfilledFns.push(() => {
  17. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  18. })
  19. }
  20. if (onRejected) {
  21. this.onRejectedFns.push(() => {
  22. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  23. })
  24. }
  25. }
  26. })
  27. }
  28. catch(onRejected) {
  29. this.then(undefined, onRejected)
  30. }

finally 方法

22_Promise使用 - 图33

我们在第一个 promise 调用 resolve(1111),运行结果如下

22_Promise使用 - 图34

但是当我们调用 catch 的时候,就不能正确执行 finally 了。如果我们在第一个 promise 调用 reject(就可以正常执行)

22_Promise使用 - 图35

这是由于在 catch 中,我们把 resolve 设置为了 undefned。就不会向下执行了。

为了不断层,我们改一下 then 方法,要返回 value

22_Promise使用 - 图36

resolve,reject 类方法

22_Promise使用 - 图37

22_Promise使用 - 图38

all 类方法

22_Promise使用 - 图39

allSettled 类方法

22_Promise使用 - 图40

运行结果

22_Promise使用 - 图41

race 方法

22_Promise使用 - 图42

any 方法

22_Promise使用 - 图43

22_Promise使用 - 图44

  1. const p1 = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. // resolve(111)
  4. reject(111)
  5. }, 4000)
  6. })
  7. const p2 = new Promise((resolve, reject) => {
  8. setTimeout(() => {
  9. // resolve(222)
  10. reject(222)
  11. }, 3000)
  12. })
  13. const p3 = new Promise((resolve, reject) => {
  14. setTimeout(() => {
  15. reject(3333)
  16. }, 2000)
  17. })
  18. HyPromise.any([p1, p2, p3])
  19. .then((res) => {
  20. console.log('res:', res)
  21. })
  22. //可以通过errors获得具体错误情况
  23. .catch((err) => {
  24. console.log('err:', err.errors)
  25. })

以上就完成了。

那么最终 Promise 的代码为

  1. const PROMISE_STATUS_FULFILLED = 'fulfilled'
  2. const PROMISE_STATUS_PENDING = 'pending'
  3. const PROMISE_STATUS_REJECTED = 'rejected'
  4. //工具函数
  5. function execFunctionWithCatchError(execFn, value, resolve, reject) {
  6. try {
  7. const result = execFn(value)
  8. resolve(result)
  9. } catch (err) {
  10. reject(err)
  11. }
  12. }
  13. class HyPromise {
  14. constructor(executor) {
  15. this.status = PROMISE_STATUS_PENDING
  16. this.value = undefined
  17. this.reason = undefined
  18. this.onFulfilledFns = []
  19. this.onRejectedFns = []
  20. const resolve = (value) => {
  21. if (this.status === PROMISE_STATUS_PENDING) {
  22. queueMicrotask(() => {
  23. if (this.status != PROMISE_STATUS_PENDING) return
  24. this.status = PROMISE_STATUS_FULFILLED
  25. this.value = value
  26. this.onFulfilledFns.forEach((fn) => {
  27. fn(this.value)
  28. })
  29. })
  30. }
  31. }
  32. const reject = (reason) => {
  33. if (this.status === PROMISE_STATUS_PENDING) {
  34. queueMicrotask(() => {
  35. if (this.status != PROMISE_STATUS_PENDING) return
  36. this.status = PROMISE_STATUS_REJECTED
  37. this.reason = reason
  38. this.onRejectedFns.forEach((fn) => {
  39. fn(this.reason)
  40. })
  41. })
  42. }
  43. }
  44. try {
  45. executor(resolve, reject)
  46. } catch (err) {
  47. reject(err)
  48. }
  49. }
  50. then(onFulfilled, onRejected) {
  51. const defaultOnRejected = (err) => {
  52. throw err
  53. }
  54. onRejected = onRejected || defaultOnRejected
  55. const defaultOnFulfilled = (value) => {
  56. return value
  57. }
  58. onFulfilled = onFulfilled || defaultOnFulfilled
  59. return new HyPromise((resolve, reject) => {
  60. if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
  61. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  62. }
  63. if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
  64. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  65. }
  66. if (this.status === PROMISE_STATUS_PENDING) {
  67. if (onFulfilled) {
  68. this.onFulfilledFns.push(() => {
  69. execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
  70. })
  71. }
  72. if (onRejected) {
  73. this.onRejectedFns.push(() => {
  74. execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
  75. })
  76. }
  77. }
  78. })
  79. }
  80. catch(onRejected) {
  81. return this.then(undefined, onRejected)
  82. }
  83. finally(onFinally) {
  84. this.then(
  85. () => {
  86. onFinally()
  87. },
  88. () => {
  89. onFinally()
  90. }
  91. )
  92. }
  93. static resolve(value) {
  94. return new HyPromise((resolve) => resolve(value))
  95. }
  96. static reject(reason) {
  97. return new HyPromise((resolve, reject) => reject(reason))
  98. }
  99. static all(promises) {
  100. return new HyPromise((resolve, reject) => {
  101. const values = []
  102. promises.forEach((promise) => {
  103. promise.then(
  104. (res) => {
  105. values.push(res)
  106. if (values.length == promises.length) {
  107. resolve(values)
  108. }
  109. },
  110. (err) => {
  111. reject(err)
  112. }
  113. )
  114. })
  115. })
  116. }
  117. static allSettled(promises) {
  118. return new HyPromise((resolve) => {
  119. const results = []
  120. promises.forEach((promise) => {
  121. promise.then(
  122. (res) => {
  123. results.push({ satus: PROMISE_STATUS_FULFILLED, value: res })
  124. if (results.length == promises.length) {
  125. resolve(results)
  126. }
  127. },
  128. (err) => {
  129. results.push({ satus: PROMISE_STATUS_REJECTED, value: err })
  130. if (results.length == promises.length) {
  131. resolve(results)
  132. }
  133. }
  134. )
  135. })
  136. })
  137. }
  138. static race(promises) {
  139. return new HyPromise((resolve, reject) => {
  140. promises.forEach((promise) => {
  141. promise.then(
  142. (res) => {
  143. resolve(res)
  144. },
  145. (err) => {
  146. reject(err)
  147. }
  148. )
  149. })
  150. })
  151. }
  152. static any(promises) {
  153. const reasons = []
  154. return new HyPromise((resolve, reject) => {
  155. promises.forEach((promise) => {
  156. promise.then(
  157. (res) => {
  158. resolve(res)
  159. },
  160. (err) => {
  161. reasons.push(err)
  162. if (reasons.length == promises.length) {
  163. reject(new AggregateError(reasons))
  164. }
  165. }
  166. )
  167. })
  168. })
  169. }
  170. }
  171. //测试代码================================================================
  172. const promise = new HyPromise((resolve, reject) => {
  173. resolve(11111)
  174. // reject(22222)
  175. })
  176. const p1 = new Promise((resolve, reject) => {
  177. setTimeout(() => {
  178. // resolve(111)
  179. reject(111)
  180. }, 4000)
  181. })
  182. const p2 = new Promise((resolve, reject) => {
  183. setTimeout(() => {
  184. // resolve(222)
  185. reject(222)
  186. }, 3000)
  187. })
  188. const p3 = new Promise((resolve, reject) => {
  189. setTimeout(() => {
  190. reject(3333)
  191. }, 2000)
  192. })
  193. HyPromise.any([p1, p2, p3])
  194. .then((res) => {
  195. console.log('res:', res)
  196. })
  197. //可以通过errors获得具体错误情况
  198. .catch((err) => {
  199. console.log('err:', err.errors)
  200. })
  201. // HyPromise.race([p1, p2, p3])
  202. // .then((res) => {
  203. // console.log('res:', res)
  204. // })
  205. // .catch((err) => {
  206. // console.log('err:', err)
  207. // })
  208. // HyPromise.allSettled([p1, p2, p3])
  209. // .then((res) => {
  210. // console.log(res)
  211. // })
  212. // .catch((err) => {
  213. // console.log(err)
  214. // })
  215. // promise
  216. // .then((res) => {
  217. // console.log('res1:', res)
  218. // return 'aaaa'
  219. // })
  220. // .catch((err) => {
  221. // console.log('err:', err)
  222. // })
  223. // .finally(() => {
  224. // console.log('finally')
  225. // })
  226. // HyPromise.resolve('hello').then((res) => {
  227. // console.log(res)
  228. // })
  229. // HyPromise.reject('err').catch((err) => {
  230. // console.log(err)
  231. // })
  232. // promise
  233. // .then(
  234. // (res) => {
  235. // console.log('res1:', res)
  236. // return 11111
  237. // },
  238. // (err) => {
  239. // console.log('err1:', err)
  240. // return 'nnnnnn'
  241. // }
  242. // )
  243. // .then(
  244. // (res) => {
  245. // console.log('res2:', res)
  246. // },
  247. // (err) => {
  248. // console.log(err)
  249. // }
  250. // )
  251. // promise.then(
  252. // (res) => {
  253. // console.log('res2:', res)
  254. // },
  255. // (err) => {
  256. // console.log('err2:', err)
  257. // }
  258. // )
  259. // setTimeout(() => {
  260. // promise.then(
  261. // (res) => {
  262. // console.log('res3:', res)
  263. // },
  264. // (err) => {
  265. // console.log('err3:', err)
  266. // }
  267. // )
  268. // }, 1000)