Promise

  • 使用Promise时,有三种状态

    • pending:进行中
    • fulfilled:已成功
    • reject:已失败

      基本使用

  • Promise是一个构造函数,用来生成Promise实例

    • 构造函数有两个参数,分别是resolve和reject。它们是两个函数,由 JS 引擎提供
      • resolve函数的作用是,将Promise对象的状态从pending变为fulfilled。在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
      • reject函数的作用是,将Promise对象的状态从pending变为reject。在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去 ```javascript // 案例一 const promise = new Promise(function(resolve, reject) { // … some code

    if (/ 异步操作成功 /){ resolve(value); } else { reject(error); } }); ```

    Promise 实例上的方法

  • Promise.prototype.then((result) => {}, (reason) => {})

    • 第一个参数是fulfilled状态的回调函数
    • 第二个参数是rejected状态的回调函数
    • 该方法返回的是一个新的Promise实例,方便链式调用 ```javascript // 案例一 let random = Math.floor(Math.random() * 100); const promise = new Promise((resolve, reject) => { if (random % 2 === 0) { resolve(‘偶数’) console.log(123) // resolve()执行后,之后的代码还是会执行 } else { reject(‘奇数’) console.log(456) // reject()执行后,之后的代码还是会执行 } })

// 链式调用 promise.then( (result) => { console.log(result); return result + ‘1’; }, (reason) => { console.log(reason); return reason + ‘1’; }, ).then( (result) => console.log(result), // 上一个then里,return后都到这个回调函数里 (reason) => console.log(reason) // 这个函数不会被用到 )

  1. - `Promise.prototype.catch(error => {})`
  2. - 作用:`.then(null, rejection)`或`.then(undefined, rejection)`的别名,用于指定发生错误时的回调函数
  3. - `catch()`方法返回的还是一个 Promise 对象,因此后面还可以接着调用`then()`方法
  4. ```javascript
  5. // 案例二
  6. let random = Math.floor(Math.random() * 100);
  7. const promise = new Promise((resolve, reject) => {
  8. if (random % 2 === 0) {
  9. resolve('偶数')
  10. } else {
  11. reject('奇数')
  12. }
  13. })
  14. promise.then(
  15. (result) => { console.log(result); throw new Error(result); },
  16. ).then(
  17. (result) => console.log(result),
  18. ).catch(err => console.log(err))
  • Promise.prototype.finally(()=>{})
    • 不管 Promise 对象最后状态如何,都会执行的操作
    • finally本质上是then方法的特例
    • 该方法是 ES2018 引入标准的 ```javascript const promise = new Promise((resolve, reject)=> …);

promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});

  1. ```javascript
  2. // Promise.prototype.finally的实现
  3. Promise.prototype.finally = function (callback) {
  4. let P = this.constructor;
  5. return this.then(
  6. value => P.resolve(callback()).then(() => value),
  7. reason => P.resolve(callback()).then(() => { throw reason })
  8. );
  9. };

Promise 构造函数上的方法

  • Promise.resolve(any)
    • 作用:将现有对象any转为 Promise 对象,状态为fulfilled
    • 参数any
      • 参数是一个 Promise 实例
        • 将不做任何修改、原封不动地返回这个实例
      • 参数是一个thenable对象(指的是具有then方法的对象)
        • 将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法
      • 参数不是具有then()方法的对象,或根本就不是对象
        • 返回一个新的 Promise 对象,状态为fulfilled
      • 不带有任何参数
        • 直接返回一个fulfilled状态的 Promise 对象
          1. // 案例一
          2. Promise.resolve('foo')
          3. // 等价于
          4. new Promise(resolve => resolve('foo'))
          ```javascript // 案例二:有then方法的对象 let thenable = { then: function(resolve, reject) { resolve(42); } };

let p1 = Promise.resolve(thenable); p1.then(function (value) { console.log(value); // 42 });

  1. - `Promise.reject(reason)`
  2. - 返回一个新的 Promise 实例,该实例的状态为`rejected`
  3. - 参数`any`
  4. - 参数是一个 Promise 实例
  5. - 将不做任何修改、原封不动地返回这个实例
  6. - 参数是一个`thenable对象`(指的是具有`then`方法的对象)
  7. - 将这个对象转为 Promise 对象,然后就立即执行`thenable对象`的`then()`方法
  8. - 参数不是具有`then()`方法的对象,或根本就不是对象
  9. - 返回一个新的 Promise 对象,状态为`rejected`
  10. - 不带有任何参数
  11. - 直接返回一个`rejected`状态的 Promise 对象
  12. <a name="Yecff"></a>
  13. ## 手写 Promise
  14. Promise 的实现标准是[ Promise/A+](https://promisesaplus.com/),该标准只规定了 then,没有规定 all、catch、race,也没有规定构造函数<br />Promise/A+ 的实现参考文献汇总 [Promise Implementations](https://promisesaplus.com/implementations)<br />我们从 promise 的使用方式开始,一步步实现 promise
  15. <a name="uXHK1"></a>
  16. ### new Promise()
  17. ```javascript
  18. // new MyPromise()
  19. new MyPromise((resolve, reject) => {
  20. resolve(123)
  21. // reject(456)
  22. })

一个 promise 实例必须是这三种状态其中之一:pending、fulfilled、rejected
2.1.1 当 promise 实例状态是 pending 时
2.1.1.1 promise 实例状态可以转变为 fulfilled、rejected 其中之一
2.1.2 当 promise 实例状态是 fulfilled 时
2.1.2.1 promise 实例不可以转变为其他状态
2.1.2.2 promise 实例必须有一个 value,且不可以改变
2.1.3 当 promise 实例状态是 rejected 时
2.1.2.1 promise 实例不可以转变为其他状态
2.1.2.2 promise 实例必须有一个 reason,且不可以改变

  1. class MyPromise {
  2. // Promise 三个状态
  3. static PENDING = 0
  4. static FULLFILLED = 1
  5. static REJECTED = 2
  6. transition(state, value) {
  7. // 状态 state 只能从 PENDING ——> FULLFILLED 或 PENDING ——> REJECTED
  8. if (this.state === state
  9. || this.state === MyPromise.FULLFILLED
  10. || this.state === MyPromise.REJECTED) {
  11. return false
  12. }
  13. this.state = state
  14. this.value = value
  15. return true
  16. }
  17. // 实现 resolve
  18. resolve(data) {
  19. // 状态变化: PENDING ——> FULLFILLED
  20. this.transition(MyPromise.FULLFILLED, data)
  21. }
  22. // 实现 reject
  23. reject(reason) {
  24. // 状态变化: PENDING ——> REJECTED
  25. this.transition(MyPromise.REJECTED, reason)
  26. }
  27. constructor(executor) {
  28. this.state = MyPromise.PENDING
  29. this.value = undefined
  30. try {
  31. executor(this.resolve.bind(this), this.reject.bind(this))
  32. } catch(error) {
  33. this.state = MyPromise.REJECTED
  34. console.error(error)
  35. }
  36. }
  37. }
  1. const promise1 = new MyPromise((resolve, reject) => {
  2. resolve(123)
  3. })
  4. const promise2 = new MyPromise((resolve, reject) => {
  5. reject(456)
  6. })
  7. // 能被内部 try catch 捕获
  8. const promise3 = new MyPromise((resolve, reject) => {
  9. throw new Error('I am Error')
  10. })
  11. // 不能被内部 try catch 捕获
  12. const promise4 = new MyPromise((resolve, reject) => {
  13. setTimeout(() => {
  14. throw new Error('I am Error')
  15. })
  16. })

:::info 为什么 MyPromise 中的异步事件 setTimeout 报错,MyPromise 内部的 trycatch 捕获不到?
答:因为 try catch 的执行是同步的,异步事件执行出错 catch 捕获不到 :::

  1. try {
  2. setTimeout(()=>{
  3. throw new Error('error')
  4. })
  5. } catch (error) {
  6. console.error(error)
  7. }
  1. class MessageCenter {
  2. constructor() {
  3. this.center = {}
  4. }
  5. // 注册事件
  6. subscribe(type, callback) {
  7. if (!this.center[type]) this.center[type] = [callback]
  8. else this.center[type].push(callback)
  9. }
  10. // 注销事件
  11. unSubscribe(type, callback) {
  12. const index = this.center[type].indexOf(callback)
  13. if (index !== -1) this.center[type].splice(index, 1)
  14. }
  15. // 发布消息
  16. public(type, ...args) {
  17. if (this.center[type]) this.center[type].forEach(callback => callback(...args))
  18. }
  19. }
  20. const messageCenter = new MessageCenter()
  21. try {
  22. function changeHandler(...args) {
  23. console.log(...args)
  24. throw Error('I am Error')
  25. }
  26. messageCenter.subscribe('change', changeHandler)
  27. } catch (error) {
  28. console.error(error)
  29. }
  30. messageCenter.public('change', 456)

then()

  1. new Promise((resolve, reject) => {
  2. resolve(123)
  3. })
  4. .then(
  5. data => console.log(data),
  6. )
  7. .then(
  8. 'data',
  9. reason => console.error(reason)
  10. )
  11. .then(
  12. data => console.log(data),
  13. reason => console.error(reason)
  14. )

根据 Promise/A+ 的 then 方法标准
一个 Promise 实例提供一个 then 方法,用于接收它 现在或最终的 值 或 错误原因

  1. promise.then(onFulfilled, onRejected)

2.2.1 onFulfilled 和 onRejected都是可选参数

  1. 如果 onFulfilled不是函数,就忽略
  2. 如果 onRejected不是函数,就忽略

2.2.2 如果onFulfilled是函数

  1. 它必须在 promise 状态变为 fulfilled 时才能执行,并且将 promise的值作为它第一个参数
  2. 在 promise 状态变为 fulfilled 之前,它不能被执行
  3. 它只能被执行一次

2.2.3 如果onRejected是函数

  1. 它必须在 promise 状态变为 rejected 时才能执行,并且将 promise的 错误原因 作为它第一个参数
  2. 在 promise 状态变为 rejected 之前,它不能被执行
  3. 它只能被执行一次

2.2.4 当执行上下文栈仅包含平台代码(platform code)时,onFulfilled 或 onRejected才能被执行 :::info platform code 指的是:JS 解析引擎、环境 和 promise 的实现代码。
在实践中,这个要求能保证在事件循环执行到 then函数时,onFulfilled 和 onRejected的执行是异步的,并使用一个新的栈。
这个 platform code 的实现可以用宏任务机制,比如[setTimeout](https://html.spec.whatwg.org/multipage/webappapis.html#timers)或 setImmediate,也可以用微任务机子来实现,比如[MutationObserver](https://dom.spec.whatwg.org/#interface-mutationobserver)或 [process.nextTick](https://nodejs.org/api/process.html#process_process_nexttick_callback) ::: 2.2.5 onFulfilled 和 onRejected必须作为函数被调用
2.2.6 then方法可以在同一个 promise 实例中,多次调用

  1. 当promise的状态是 fulfilled,所有 onFulFilled回调函数,必须按照它们各自所在then函数的顺序,依次执行
  2. 当promise的状态是 rejected,所有 onRejected回调函数,必须按照它们各自所在then函数的顺序,依次执行

2.2.7 then必须返回一个 promise 实例

  1. 不论是 onFulfilled还是 onRejected返回了一个值 x,执行 Promise 决议程序,即[[Resolve]](promise2, x)
  2. 不论是 onFulfilled还是 onRejected抛出一个异常 e,promise2必须以 e作为错误原因变为 rejected 状态
  3. 如果 onFulfilled不是函数,并且 promise1状态为 fulfilled。promise2必须以 promise1的value 作为 value,并变为 fulfilled 状态
  4. 如果 onRejected不是函数,并且 promise1状态是 rejected。promise2必须以 promise2的 reason 作为 reason,并变为 rejected 状态
    1. promise2 = promise1.then(onFulfilled, onRejected);
    :::info Promise Resolution Procedure:Promise 决议程序是一个以输入为一个 promise 和 一个 value 的抽象操作,我们用 [[Resolve]](promise, x)来描述这一操作。如果 x是一个 thenable 对象,它会假设 x的行为有点像一个 promise 实例,并试图让 promise采用 x 的状态。否则,它将以 x作为 promise的 value,并将 promise状态变为 fulfilled。

在执行 [[Resolve]](promise, x)时,按照以下步骤执行
2.3.1 如果 promise和 x指向同一个对象,那么以 TypeError作为理由拒绝 promise
2.3.2 如果 x是一个 promise,采用它的状态

  1. 如果 x是 pending 状态,promise必须保留 pending 状态直到,x状态变为 fulfilled/rejected
  2. 如果 x是 fulfilled 状态,将 promise状态变为 fulfilled,并返回相同的 value
  3. 如果 x是 rejeceted 状态,将 promise状态变为 rejected,并返回相同的 reason

2.3.3 否则,如果 x是一个对象或函数

  1. 令 then等于 x.then
  2. 如果检索 x.then这个属性而导致抛出异常 e,则将 promise状态变为 rejected,并以 e作为 reason
  3. 如果 then是一个函数,那么将它的 this指向 x,第一个参数为 resolvePromise,第二个参数为 rejectPromise
    1. 如果 resolvePromie以一个y作为 value 被调用,则执行 [[Resolve]](promise, y)
    2. 如果 rejectPromise以一个 r作为 reason 被调用,以 r为理由返回 rejected 状态的 promise
    3. 如果resolvePromie和rejectPromise同时被调用了,或者对其中一个多次调用,那么以第一次调用为主,忽略之后的调用
    4. 如果调用 then方法抛出异常 e
      1. 如果resolvePromie和rejectPromise被调用过了,忽略它
      2. 否则,以 e为理由返回 rejected 状态的 promise
  4. 如果 then 不是一个函数,则将promise状态变为 fulfilled,并以 x为值

2.3.4 如果 x 不是对象或函数,则将promise状态变为 fulfilled,并以 x为值 ::: 按照 Promise/A+ 规范的要求,我们先来实现 then 函数的一部分功能,第 2.2.1 ~ 2.2.6 点,测试用例如下

  1. const promise1 = new MyPromise((resolve, reject) => {
  2. reject(123)
  3. })
  4. promise1.then(
  5. data => console.log(data, 1),
  6. reason => {
  7. console.log(reason + 'error1')
  8. return 555
  9. }
  10. )
  11. promise1.then(
  12. 'data',
  13. reason => console.log(reason + 'error2')
  14. )
  1. class MyPromise {
  2. // Promise 三个状态
  3. static PENDING = 0
  4. static FULLFILLED = 1
  5. static REJECTED = 2
  6. transition(state, value) {
  7. // 状态 state 只能从 PENDING ——> FULLFILLED 或 PENDING ——> REJECTED
  8. if (this.state === state
  9. || this.state === MyPromise.FULLFILLED
  10. || this.state === MyPromise.REJECTED) {
  11. return false
  12. }
  13. this.state = state
  14. this.value = value
  15. return true
  16. }
  17. // 实现 resolve
  18. resolve = (data) => {
  19. // 状态变化: PENDING ——> FULLFILLED
  20. const stateChange = this.transition(MyPromise.FULLFILLED, data)
  21. if (stateChange) {
  22. while (this.onFulfilledQueue.length) {
  23. const onFulfilled = this.onFulfilledQueue.shift()
  24. onFulfilled(this.value)
  25. }
  26. }
  27. }
  28. // 实现 reject
  29. reject = (reason) => {
  30. // 状态变化: PENDING ——> REJECTED
  31. const stateChange = this.transition(MyPromise.REJECTED, reason)
  32. if (stateChange) {
  33. while (this.onRejectedQueue.length) {
  34. const onRejected = this.onRejectedQueue.shift()
  35. onRejected(this.value)
  36. }
  37. }
  38. }
  39. constructor(executor) {
  40. this.state = MyPromise.PENDING
  41. this.value = undefined
  42. this.onFulfilledQueue = []
  43. this.onRejectedQueue = []
  44. try {
  45. // executor 的执行时机需要比 then 晚,then 方法才能先注册 onFulfilled, onRejected 事件
  46. // 这里使用 setTimeout 来将 executor 放入宏任务队列中等待执行
  47. // 当主线程中的 then 方法执行完成后,再触发 executor
  48. const timer = setTimeout(() => {
  49. executor(this.resolve, this.reject)
  50. clearTimeout(timer)
  51. })
  52. } catch (error) {
  53. this.state = MyPromise.REJECTED
  54. console.error(error)
  55. }
  56. }
  57. then(onFulfilled, onRejected) {
  58. if (typeof onFulfilled === 'function') this.onFulfilledQueue.push(onFulfilled)
  59. if (typeof onRejected === 'function') this.onRejectedQueue.push(onRejected)
  60. }
  61. }

:::warning ⭐注意:上面代码用了 setTimeout 来让 executor 执行时机比 then 晚,但这会导致两个问题。

  1. try catch 是同步的,不能捕获 executor 执行时抛出的错误。怎么解决我们下文再说。
  2. 原生 Promise 的 executor 是同步执行的,而我们实现的 executor 现在变成异步执行的 ::: 目前,由于 then 没有返回任何东西,如果我们像下面这样调用 then 函数的话,会有问题。 ```javascript const promise1 = new MyPromise((resolve, reject) => { reject(123) })

const promise2 = promise1.then( ‘data’, reason => reason + ‘error’ )

promise2.then( data => console.log(data), reason => console.error(reason) )

  1. 接下来,我们需要实现 Promise/A+ 规范的第 2.2.7 点。
  2. > Promise 的 then 方法链式调用,为什么不能直接返回 this ?
  3. > 答:根据 Promise/A+ 规范的第 2.2.2、2.2.3 点,`onFulFilled`和`onRejected`只能在 promise 实例状态由 pending 变为 fulfilled/rejected 时,才能被调用。直接返回 this,promise 状态不对
  4. ```javascript
  5. class MyPromise {
  6. // Promise 三个状态
  7. static PENDING = 0
  8. static FULLFILLED = 1
  9. static REJECTED = 2
  10. transition(state, value) {
  11. // 状态 state 只能从 PENDING ——> FULLFILLED 或 PENDING ——> REJECTED
  12. if (this.state === state
  13. || this.state === MyPromise.FULLFILLED
  14. || this.state === MyPromise.REJECTED) {
  15. return false
  16. }
  17. this.state = state
  18. this.value = value
  19. return true
  20. }
  21. // 实现 resolve
  22. resolve = (data) => {
  23. // 状态变化: PENDING ——> FULLFILLED
  24. const stateChange = this.transition(MyPromise.FULLFILLED, data)
  25. if (stateChange) {
  26. while (this.onFulfilledQueue.length) {
  27. const onFulfilled = this.onFulfilledQueue.shift()
  28. onFulfilled(this.value)
  29. }
  30. }
  31. }
  32. // 实现 reject
  33. reject = (reason) => {
  34. // 状态变化: PENDING ——> REJECTED
  35. const stateChange = this.transition(MyPromise.REJECTED, reason)
  36. if (stateChange) {
  37. while (this.onRejectedQueue.length) {
  38. const onRejected = this.onRejectedQueue.shift()
  39. onRejected(this.value)
  40. }
  41. }
  42. }
  43. constructor(executor) {
  44. this.state = MyPromise.PENDING
  45. this.value = undefined
  46. this.onFulfilledQueue = []
  47. this.onRejectedQueue = []
  48. try {
  49. executor(this.resolve, this.reject)
  50. } catch (error) {
  51. this.state = MyPromise.REJECTED
  52. this.reject(error)
  53. }
  54. }
  55. then(onFulfilled, onRejected) {
  56. if (typeof onFulfilled !== 'function') onFulfilled = data => data
  57. if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
  58. const promise1 = this
  59. // return 的就是 promise2
  60. const promise2 = new MyPromise((resolve, reject) => {
  61. // 1. executor 执行时机要比 then 晚
  62. // 2. then 中的回调函数执行时机要比 executor 晚
  63. const onFulfilledTask = () => {
  64. const timer = setTimeout(() => {
  65. try {
  66. const x = onFulfilled(promise1.value)
  67. MyPromise.PromiseResolution(promise2, x, resolve, reject)
  68. clearTimeout(timer)
  69. } catch (error) {
  70. reject(error)
  71. clearTimeout(timer)
  72. }
  73. })
  74. }
  75. const onRejectedTask = () => {
  76. const timer = setTimeout(() => {
  77. try {
  78. const x = onRejected(promise1.value)
  79. MyPromise.PromiseResolution(promise2, x, resolve, reject)
  80. clearTimeout(timer)
  81. } catch (error) {
  82. reject(error)
  83. clearTimeout(timer)
  84. }
  85. })
  86. }
  87. try {
  88. if (promise1.state === MyPromise.FULLFILLED) {
  89. onFulfilledTask()
  90. } else if (promise1.state === MyPromise.REJECTED) {
  91. onRejectedTask()
  92. } else if (promise1.state === MyPromise.PENDING) {
  93. promise1.onFulfilledQueue.push(onFulfilledTask)
  94. promise1.onRejectedQueue.push(onRejectedTask)
  95. }
  96. } catch (error) {
  97. // 按照 Promise/A+ 的 2.2.7 的 b 点
  98. // 不论是 onFulfilled 还是 onRejected 抛出一个异常 e,promise2 必须以 e 作为错误原因变为 rejected 状态
  99. reject(error)
  100. }
  101. })
  102. return promise2
  103. }
  104. // Promise Resolution Procedure
  105. static PromiseResolution(promise2, x, resolve, reject) {
  106. if (promise2 === x) reject(new TypeError())
  107. else if (x === null) resolve(x)
  108. // 不论 x 是 promise 实例,还是用户自定义 thenable 对象/函数,都执行如下逻辑
  109. else if (['object', 'function'].includes(typeof x)) {
  110. let then
  111. try {
  112. then = x.then
  113. } catch (error) {
  114. reject(error)
  115. }
  116. if (typeof then !== 'function') {
  117. resolve(x)
  118. } else {
  119. // 根据 2.3.3 的 第 iii 点,使用 firstCall
  120. // 使得只执行第一次 resolvePromise/rejectPromise 调用
  121. let firstCall = true
  122. try {
  123. then.call(x,
  124. // resolvePromise
  125. (y) => {
  126. if (!firstCall) return
  127. firstCall = false
  128. MyPromise.PromiseResolution(promise2, y, resolve, reject)
  129. },
  130. // rejectPromise
  131. (r) => {
  132. if (!firstCall) return
  133. firstCall = false
  134. reject(r)
  135. })
  136. }
  137. catch (error) {
  138. if (!firstCall) return
  139. reject(error)
  140. }
  141. }
  142. }
  143. else {
  144. resolve(x)
  145. }
  146. }
  147. }
  148. module.exports = MyPromise

测试 then 方法是否合乎标准

使用 promises-aplus-tests 库,测试手写的 Promise 是否合乎 Promise/A+ 规范

  1. // 添加 deferred 方法,promises-aplus-tests 会去调用 MyPromise 执行测试用例
  2. MyPromise.deferred = function () {
  3. var result = {};
  4. result.promise = new MyPromise(function (resolve, reject) {
  5. result.resolve = resolve;
  6. result.reject = reject;
  7. });
  8. return result;
  9. }
  1. {
  2. "name": "MyPromise",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "index.js",
  6. "scripts": {
  7. "test": "promises-aplus-tests index.js"
  8. },
  9. "keywords": [],
  10. "author": "",
  11. "license": "ISC",
  12. "devDependencies": {
  13. "promises-aplus-tests": "^2.1.2"
  14. }
  15. }

执行完 npm run test,看到类似下面情况,说明测试用例都通过了
image.png