1、什么是Promise
Promise是前端目前主流的异步操作解决方案,实质上是一个有状态的JS对象,分为三种状态:
- pending(初始状态)
- fulfilled(成功状态)
- rejected(失败状态)
当我们通过 Promise 进行异步操作后,会改变 Promise 对象的状态(要么为 fulfilled,要么为 rejected),之后可以调用相应的回调(fulfilled ---> then, rejected ---> catch),在回调中对异步操作的结果进行处理。
2、Promise 的基础结构及流程
俗话说的好:"工欲善其事,比先利其器",在开始学习 Promise 前,个人认为可以先了解 Promise 基础结构和流程,方便后续的学习与理解(如果对 Promise 源码感兴趣,可以看这篇文章)
2.1、Promise 的基础结构
2.2、Promise 的流程
通过 new 创建 Promise对象时
- 需要传入一个回调函数,叫做 executor()
- executor()回调函数会被立即执行,并且它会传入两个回调 resolve()、reject()
- 当调用 resolve() 时,会执行 Promise对象的 then()
- 当调用 reject() 时,会执行 Promise对象的 catch()
注意:
- 通常会在 executor() 中 确定 Promise 的状态,即 成功状态(fulfilled)、失败状态(rejected)
- 只要 Promise 对象的状态被确定了,状态就不会再改变了,也就是说,Promise 对象的状态只能改变一次!
Promise 的整个流程图解如下:
那么,在了解了 Promise 是个啥,长个啥样,接下来正式进入 Promise 的学习!
3、resolve()
3.1、resolve() 是干嘛的呢?请先看下面一段代码:
上面代码的输出结果如下:
那么我们来简单分析下:
- 我们调用了 resolve 方法,并且传入了一个参数 '啊啊啊啊啊啊啊',那么这一个操作的结果是改变了 Promise 对象的状态, 为 fulfilled
- 再由前面的流程图可知,fulfilled 时,会走 then 回调中, 此时输出 val 参数,值是 '啊啊啊啊啊啊啊'
所以,resolve 方法可以理解为:将 Promise 成功的结果传入 then 回调的一个通道
3.2、resolve() 方法的参数
resolve(params) 的参数有三种情况
- 普通的值,如 string、number、boolean等
- promise 对象
- 实现了 thenable 方法的对象
3.2.1、 params 为普通的值
如:当 params 分别为 number、string、obj 时
上面代码的输出结果为:
实际上,当 params 为普通的值时,Promise 会通过 Promise.resolve(params) 把 params 包装成一个新的 Promise 对象并返回,即:
3.2.2、 params 为 Promise
如:当 params 为 Promise 对象时
上面代码的输出结果为:
这时候有的小伙伴可能会有疑问:为什么输出的是 '成功的man' 而不是 '失败的man' ?
还记得我们上面 2.2 中说过的一句话吗?Promise 的状态一旦更改,就不能改变!,所以上面的代码,当执行了 resolve() 后, Promise 的状态为 fulfilled,状态改变,则调相应的回调 then()
而如果把上面 resolve、 reject 的执行顺序反过来呢?
输出结果就是 '失败的man' 了!
所以当 params 为 Promise 对象时,会根据 params 的状态来决定原本 Promise 的状态,从而再去执行 then 或者 reject (记好笔记!!)
3.2.3、 params 为实现了 thenable 方法的对象
直接上代码:
由上面的代码可知:当 thenableObject.then() 中执行了 resolve(),则原本的 Promise 对象为 fufilled,执行 then() 回调,否则执行 catch() 回调
同样,如果 params 为实现了 thenable 方法的对象,那么会根据这个 对象的状态 去决定原本 Promise 对象的状态
4、then()
4.1、then() 是干嘛的呢?请先看下面一段代码:
由上面的代码可知,原来 then(onFulfilled, onRejected) 接受了两个回调函数 callback 作为参数,当 p 的状态是 fulfilled 时,调用 onFulfilled, 当 p 的状态是 rejected 时,调用 onRejected
4.2、then() 的链式调用
我们知道,Promise 是用来解决回调地狱问题的可靠手段,那么具体是怎么解决的呢?话不多说,直接上代码:
此时聪明可爱的小伙伴会发现,欸?!他妈的输出的结果成了 456 ?!这又是为什么呢?这就是我们下面要说的,在 then() 中,我们可以通过 return x 来实现 链式调用
4.3、then() 的返回值
then() 的返回值,其实也有三种情况
- 普通的值,如 string、number、boolean等
- promise 对象
- 实现了 thenable 方法的对象
有没有发现和 resolve(params) 的情况一样?那么接下来,我们稍微节奏快一点咯~
4.3.1、then 的返回值是普通的值
这种情况下,直接把上一个 then() 方法中 return 的值传给了下一个 then(val) 的 val,实际上,上面代码中的
return '乔瑟夫-乔斯达'
//等价于
return new Promise(resolve => resolve('乔瑟夫-乔斯达'))
也就是说,then() 返回 普通值的时候,会被包装成 Promise 对象返回,这样就可以调用下一次 then() 方法了
4.3.2、then 的返回值是 promise
上代码:
由上面的代码可知:
- 当 then() 返回的是 fulfilled 的 Promise 对象时,会调用下一次 then(onFulfilled, onRejected) 的 onFulfilled 回调,并且将 resolve(params) 的 params 传给 onFulfilled
- 当 then() 返回的是 rejected 的 Promise 对象时,会调用下一次 then(onFulfilled, onRejected) 的 onRejected 回调,并且将 resolve(params) 的 params 传给 onRejected
4.3.3、then 的返回值是实现了 thenable 方法的对象
还是上代码:
由上面的代码可知,当 then() 返回的是实现了 thenable 方法的对象时,会根据该对象结果的状态,决定原 Promise 对象的状态
- 若 thenable 对象执行的结果是 fulfiiled,则原 Promise 对象的状态为 fulfilled,则调用then(onFulfilled, onRejected) 的 onFulfilled 回调,并且将 thenable 中 resolve(params) 的 params 传给 onFulfilled
- 若 thenable 对象执行的结果是 rejected,则原 Promise 对象的状态为 rejected,则调用then(onFulfilled, onRejected) 的 onRejected 回调,并且将 thenable 中 resolve(params) 的 params 传给 onRejected
4.4、总结
一句话:
- then 链式调用时,根据上一次 then 的返回值的状态,决定下一个 then 是调用 onFulfilled 还是 onRejeted!
5、catch()
5.1、catch() 是干嘛的?请先看下面的代码
由上面的代码可知:catch() 是处理 Promise 失败时的回调,并且也会返回一个新的 Promise 实现链式调用
5.2、catch() 返回值
catch() 和 then() 一样,也能进行链式调用,且 catch 的返回值有四种情况
- 普通的值,如 string、number、boolean等
- promise 对象
- 实现了 thenable 方法的对象
- 抛出错误 throw new Error()
经过前两个方法的学习,我觉得你应该能直接通过阅读代码来理解 catch 的使用了~
5.2.1、catch 的返回值是普通的值
5.2.2、catch 的返回值是 promise
注意:并不是说调用了 catch() 后,Promise 就是 rejected 了,也需要根据 catch 中的返回值的状态来决定的
当返回的 Promise 是 fulfilled 时:
当返回的 Promise 是 rejeted 时:
所以,一定要注意!并不是说调用了 catch() 后,Promise 就是 rejected 了
5.2.1、catch 的返回值是实现了 thenable 方法的对象
同样,如果 catch 返回的是 实现了 thenable 方法的对象,则会根据 thenable.then() 的结果的状态,决定下一次是调用 then 还是 catch
6、finally()
finally() 就很简单了,它表示在 then()、catch() 之后的逻辑 上代码:
不管 Promise 是 fulfilled 还是 rejected,最终都会执行 finally(),实际项目中我们可以在 finally() 里进行取消订阅、关闭弹窗之类的操作
提问:如果你在 finally() 里面又返回了一个 promise 会怎么样呢?
答案是:拿不到 val
7、all()
7.1、all() 是干嘛的呢?请看下面一段代码
原来 Promise.all() 是接受多个 Promise 对象作为参数,将这些 Promise 对象的结果放到一个数组里面,并且返回。 那么接下来我们学习,到底是怎么样收集的呢?
7.2、all() 的规则
当所有的 Promise 对象 都为 fulfilled 时,新的 Promise 对象为 fulfilled 并且会将参数中所有 Promise 的 返回值 组成一个数组返回
当所有的 Promise 对象中,有一个 Promise 是 rejected ,那么新的 Promise 对象为 rejected, 并且会将 第一个 rejected 的返回值作为参数,然后去执行 catch(err)
直接上代码理解:
- 当 p1、p2、p3 均为 fulfilled 时:
- 当 p1、p2、p3 有一个为 rejected 时:
7.3、all() 的缺陷
当其中一个 Promise 为 rejected 时,那么新的 Promise 对象 为 rejected,对于剩余的 fulfilled 以及 pending 状态的 Promise 我们是获取不到的
8、race()
Promise.race() 和 Promise.all() 类似,都是接受 多个 promise 对象作为参数,只不过谁先有结果就用谁的结果
上代码:
上述代码中,我们通过 setTimeout 来模拟数据请求操作,p2 经过一秒钟后先有结果(或者说 p2 的状态最先改变),将结果传给 resolve(),输出的结果是 b
所以, Promise.race() 的作用:包裹多个Promise,且谁先有结果,就使用谁的结果
9、allSettled()
Promise.allSettled() 与前面两个方法也是类似的
9.1、allSettled() 的规则
- 接受多个 promise 对象作为参数
- 等所有的 promise 对象都有结果后(或者说状态都改变后),原 Promise 对象才有结果
- 会把所有 promise 对象的 状态、值 作为对象,添加到数组中
- 返回这个数组,原 Promise 对象的状态为 fulfilled
需要注意:只要调用了 allSettled(),则 Promise 一定是 fulfilled
上代码:
10、any()
Promise.any() 和 Promise.race() 方法类似,规则如下
- 接受多个 promise 对象作为参数
- 等到参数中至少有一个 Promise 的状态为 fulfilled,原 Promise 的状态才为 fulfilled,此时就可以去执行 then 方法
- 如果参数中所有的 Promise 都是 rejected 或者传入一个空数组 ,那么会报错AggregateError
上代码:
- 至少一个 Promise 为 fulfiiled 时
- 所有 Promise 都为 rejected 时
- 传入空数组时
结尾
到这里后,你就掌握了 Promise 的使用啦!快去运用到项目中吧!
如果 这篇文章 对你有帮助,欢迎 点赞、收藏、评论,如果有 错误或还有疑问的地方,请 评论留言或者私信!