Promise

Promise是一个类,可以翻译成承诺、许诺、期约

基本使用方法

  1. // resolve , reject 是两个回调函数
  2. // 当我们调用resolve的时候 , 会执行Promise对象的then方法传入的回调函数
  3. // 当我们调用reject的时候 , 会执行Promise对象的catch方法传入的回调函数
  4. const promise = new Promise((resolve, reject) => { })

Promise的状态一旦被确定下来,无法被更改,resolve、reject两个函数不会代码禁止向下执行,为了防止继续向下执行,要加上return

Promise状态

  • 待定pending
  • 已兑现fulfilled
  • 已拒绝rejected

resolve不同值的区别

  1. 传入普通值或对象,会直接作为then回调的参数

  2. 传入另一个Promise,那么新Promise对象会决定原Promise的状态 ```javascript const promise = new Promise((resolve, reject) => {

    resolve(new Promise((resolve, reject) => {

    1. reject("我是新的promies, 我要改变之前promise的状态,这里会执行err")

    })) });

promise.then(res => { console.log(res) }, err => { console.log(err) })

  1. 3.
  2. 传入一个实现了then方法的对象,则会执行该then方法,并根据then方法的结果来决定`Promise`状态
  3. ```javascript
  4. const promise = new Promise((resolve, reject) => {
  5. const obj = {
  6. name: 'obj',
  7. then(resolve, reject) {
  8. reject('传入一个对象,对象里面有then方法,会改变之前promise的状态')
  9. }
  10. }
  11. resolve(obj)
  12. });
  13. promise.then(res => {
  14. console.log(res)
  15. }, err => {
  16. console.log(err)
  17. })

then方法

then方法实质是对象上的方法,也就是Promise.prototype.then

1.同一个Promise可以被多次调用then方法

  1. const promise=new Promise((resolve,reject)=>{
  2. resolve('abc')
  3. })
  4. promise.then((res)=>{
  5. console.log('res1'+res)
  6. })
  7. promise.then((res)=>{
  8. console.log('res2'+res)
  9. })
  10. /*
  11. res1:abc
  12. res2:abc
  13. */

2.then方法传入的回调函数可以有返回值

then方法的返回值会被作为一个新Promise对象的resolve值返回,这也是为何能链式调用Promise的then方法,则返回值的不同情况与上文resolve的三种不同返回值类似

  1. 如果返回的是一个普通值(函数默认返回值undefined),那么这个值会作为一个新的Promiseresolve
  1. const promise=new Promise((resolve,reject)=>{
  2. resolve('abc')
  3. })
  4. promise.then(res=>{
  5. return 'aaa'
  6. /* 等价于
  7. return new Promise((resolve)=>{
  8. resolve('aaa')
  9. })
  10. */
  11. })
  1. 如果返回一个Promise对象 ,其依旧将此值作为一个新的Promise对象的resolve值,由返回的Promise对象决定其状态
  2. 如果返回一个实现了then方法的Promise对象,同样作为新Promise对象的resolve值,执行then方法,并根据then方法的结果来决定Promise状态

catch方法

then方法可以传入两个回调函数,分别作为正确执行的回调函数和捕获错误的回调函数,但会使代码阅读性不佳

因此类似一种语法糖,可以使用catch方法来传入捕获错误的回调函数,而then方法仅传入正确执行的回调函数

1.当executor抛出错误时,也是会调用错误捕获的回调函数

  1. const promise=new Promise((resolve,reject)=>{
  2. // reject('rejected')
  3. throw new Error('rejected')
  4. })
  5. promise.then(undefined,err=>{
  6. console.log('err:'+err)
  7. })

2.拒绝捕获的问题

多次使用Promise的then/catch方法,捕获是分别进行的,如下则存在没有捕获错误的问题

  1. const promise=new Promise((resolve,reject)=>{
  2. reject('rejected')
  3. })
  4. promise.then(res=>{···}) // 无捕获
  5. promise.catch(err=>{···}) // 有捕获
  6. /* 正确使用 */
  7. // 方式一
  8. promise.then(res=>{···},
  9. err=>{···}
  10. )
  11. // 方式二
  12. promise
  13. .then(res=>{···})
  14. .catch(err=>{···})

3.catch方法返回值

实际上catch方法也是会返回一个Promise对象,因此类似then方法,catch方法的返回值会作为一个新Promise对象的resolve值返回

finally方法

ES9(2018)中新增特性:表示无论Promise对象为何种状态,最终都会执行的代码

1.finally的返回值

实测没有任何效果,并不影响链式调用,finally方法下一个链式调用的then方法的参数依旧为finally方法的上一个方法返回值

Promise.resolve(),Promise.reject()

Promise.resolve()

相当于new Promise,并且执行resolve操作,不同于一般resolve操作,其返回值不做任何处理直接作为参数

Promise.reject()

同理

Promise.all(),Promise.allSettled()

Promise.all()

所有Promise都变成fulfilled状态后,再拿到结果数组

但当任意 Promise变成rejected状态,则立即变成对应的rejected状态

  1. Promise.all([p1,p2,p3])
  2. .then(resArr=>{
  3. console.log(resArr) // [p1Res,p2Res,p3Res]
  4. }).catch(err=>{
  5. console.log(err) // errMessage
  6. })

Promise.allSettled()

ES11(ESMA2020)新增Promise API

其目的是弥补promise.all()的缺陷,该方法中所有Promise无论状态如何最终均不影响该Promise结果一定为fulfilled状态

  1. Promise.allSettled([p1,p2,p3])
  2. .then(resArr=>{
  3. console.log(resArr)
  4. /*
  5. [
  6. {status:'fullfilled',value:p1Res},
  7. {status:'rejected',value:p2Res},
  8. {status:'fullfilled',value:p3Res},
  9. ]
  10. */
  11. })

Promise.race(),Promise.any()

Promise.race()

race可翻译为竞赛,只看最先状态变化的Promise

该方法只要检测到传入的任意Promise首先变为任意状态,则不再等待其余Promise,直接返回最先状态发生变化的Promise对应的状态和结果

  1. Promise.race([p1,p2,p3])
  2. .then(res=>{
  3. console.log(res)
  4. }).catch(err=>{
  5. console.log(err)
  6. })

Promise.any()

ES12(ECMA2021)新增Promise API,目的用于弥补Promise.race()缺陷

该方法不同于Promise.race()只等待第一个Promise状态变化,其会等待第一个状态变为fulfilledPromise

但如果传入的Primise均为rejected状态,也会等待所有状态变化后再返回AggregateError错误(其属性errors可查看所有rejected返回值)

  1. Promise.race([p1,p2,p3])
  2. .then(res=>{
  3. console.log(res)
  4. }).catch(err=>{
  5. console.log(err.errors) // [p1Err,p2Err,p3Err]
  6. })

简易Promise实现

Promise规范

Promises/A+ (promisesaplus.cn)

拥有规范才能统一使用方法和相关细节,不然每个人实现的Promise可能都不一样

实现代码

  1. /* 状态常量定义 */
  2. const PROMISE_STATUS_PENDING='pending'
  3. const PROMISE_STATUS_FULFILLED='fulfilled'
  4. const PROMISE_STATUS_REJECTED='rejected'
  5. class HYPromise{
  6. constructor(executor){
  7. /* Promise状态属性 */
  8. this.status=PROMISE_STATUS_PENDING;
  9. /* Promise resolve reject函数参数属性
  10. 用于保存函数参数,在不同地方调用 */
  11. this.value=undefined;
  12. this.reason=undefined;
  13. /* Promise 回调函数存储结构属性
  14. 1.由于Promise中可存在异步executor,需先保存then方法传入的回调函数,待异步executor改变Promise状态后再调用
  15. 2.但理由1中仅需保存一个函数即可,此处为数组的理由是考虑Promise多次调用,存在多次调用then方法,所以由数组储存 */
  16. this.onFulfilledFns=[];
  17. this.onRejectedFns=[];
  18. /* Promise 内置resolve方法 */
  19. const resolve=(value)=>{
  20. if(this.status===PROMISE_STATUS_PENDING){
  21. // queueMicrotask:添加微任务
  22. queueMicrotask(()=>{
  23. /* 再次判断当前状态,处理executor中改变多次状态但仅第一次有效情况,
  24. 由此可能executor中产生多个resolve和reject微任务,需再次确认状态为pending */
  25. if(this.status!==PROMISE_STATUS_PENDING)
  26. return;
  27. this.status=PROMISE_STATUS_FULFILLED;
  28. /* 保存当前Promise resolve函数参数,为之后情况多次调用 */
  29. this.value=value;
  30. this.onFulfilledFns.forEach(fn=>{
  31. fn(this.value)
  32. })
  33. })
  34. }
  35. }
  36. /* Promise 内置reject方法 */
  37. const reject=(reason)=>{
  38. if(this.status===PROMISE_STATUS_PENDING){
  39. queueMicrotask(()=>{
  40. if(this.status!==PROMISE_STATUS_PENDING)
  41. return;
  42. this.status=PROMISE_STATUS_REJECTED;
  43. this.reason=reason;
  44. this.onRejectedFns.forEach(fn=>{
  45. fn(this.reason)
  46. })
  47. })
  48. }
  49. }
  50. /* 处理executor内部抛出错误情况也会触发reject */
  51. try{
  52. executor(resolve,reject)
  53. }catch(err){
  54. reject(err)
  55. }
  56. }
  57. /* then方法 */
  58. then(onFulfilled,onRejected){
  59. /* 解决 onFufilled,onRejected 没有传值的问题
  60. 并且由于catch其实是捕获的下一个Promise,此处onRejected默认为抛出当前异常,可实现then/catch功能 */
  61. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
  62. onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
  63. /* Promise的then方法返回新的Promise对象,实现链式调用
  64. 注意此处为箭头函数,因此当前箭头函数的this绑定为then方法作用域的 */
  65. return new HYPromise((resolve,reject)=>{
  66. /* 处理多次调用情况下当前Promise状态已发生改变 */
  67. if(this.status===PROMISE_STATUS_FULFILLED && onFulfilled){
  68. try{
  69. /* 将当前Promise resolve中保存的this.value调用then方法传入的回调函数 */
  70. const value=onFulfilled(this.value);
  71. /* 并将回调函数返回的值作为新Promise对象的返回值传入下一then方法中 */
  72. resolve(value);
  73. }catch(err){
  74. /* 若回调函数发生错误则新Promise对象改变为rejected状态 */
  75. reject(err);
  76. }
  77. }
  78. if(this.status===PROMISE_STATUS_REJECTED && onRejected){
  79. try{
  80. const reason=onRejected(this.reason);
  81. resolve(reason);
  82. }catch(err){
  83. reject(err);
  84. }
  85. }
  86. /* 当前Promise为首次手动创建时,就会以pending状态执行当前if语句
  87. 此时可能由于异步executor或微任务resolve,reject导致暂未确定状态,因此需保存回调函数待之后的调用 */
  88. if(this.status===PROMISE_STATUS_PENDING){
  89. /* 保存resolve回调函数
  90. 1.注意若不考虑链式调用的情况下可能只需保存onFulfilled函数即可,但考虑到链式调用
  91. 此处应封装下onFulfilled函数,由此可以获取到其返回值value,并改变新Promise状态进入链式调用 */
  92. this.onFulfilledFns.push(()=>{
  93. try{
  94. const value=onFulfilled(this.value)
  95. resolve(value)
  96. }catch(err){
  97. reject(err)
  98. }
  99. })
  100. this.onRejectedFns.push(()=>{
  101. try{
  102. const reason=onRejected(this.reason)
  103. resolve(reason)
  104. }catch(err){
  105. reject(err)
  106. }
  107. })
  108. }
  109. })
  110. }
  111. /* catch方法 */
  112. catch(onRejected){
  113. /* 其实此处catch其实是对下一个Promise的catch,
  114. 配合默认onRejected可实现catch方法捕获当前的异常 */
  115. return this.then(undefined,onRejected);
  116. }
  117. /* finally方法 */
  118. finally(onFinally) {
  119. return this.then(
  120. () => { onFinally() },
  121. err => { onFinally(); throw err; }
  122. );
  123. }
  124. /* 静态resolve方法 */
  125. static resolve(value){
  126. return new HYPromise((resolve)=>resolve(value));
  127. }
  128. /* 静态reject方法 */
  129. static reject(reason){
  130. return new HYPromise((resolve,reject)=>reject(reason));
  131. }
  132. /* 静态all方法 */
  133. static all(.){
  134. }
  135. }