Promise
📗 概述
🔬 工作模型
使用异步任务的主要目的为避免阻塞主进程的执行,每个异步任务都可以被封装成一个Promise对象,这个对象可以视作一个代表异步操作结果的占位符。如下这些常见类型的任务都可以被视作为Promise对象。
每个Promise对象都存在两种阶段及三种状态,且具有以下特点。
- Promise对象总是从未决阶段改变为已决阶段,即任务总是从挂起状态改变为已兑现或已拒绝状态,无法逆向变化。
- 当一个Promise对象处于已决阶段,即已兑现或已拒绝状态,它的状态将永远保持不变。
- 已兑现意味着异步任务操作成功完成,已拒绝意味着异步任务操作失败。
当创建一个Promise对象时,它的初始状态为挂起(pending)。从挂起状态到已兑现状态(fulfilled)的过程称为resolve
;从挂起状态到已拒绝状态(rejected)的过程称为reject
。且这两个过程都可以传递一个数据,来表示当前Promise对象成功后的数据data
或者失败时的原因reason
。
每个Promise对象都可以根据其异步操作的结果来进行后续任务处理,针对成功的后续任务称为onFulfilled
,针对失败的后续任务称为onRejected
,两者类型都为函数。且当函数调用时,会传入当前Promise对象相关联的数据,即data
或者reason
。
🏃♂️ 创建实例
创建实例可以使用Promise的构造函数(new关键字),该方式主要用于封装一个尚未支持链式调用的Promise对象。构造函数接收一个执行函数executor
。
在创建实例时,executor
函数同步执行,且在被调用时会传入两个参数,分别为resolve
和reject
,用于改变当前Promise对象的状态和数据。resolve
能够兑现当前Promise对象,reject
能够拒绝当前Promise对象。
若executor
执行时抛出错误,则当前Promise对象将被拒绝,原因为异常对象。以下列举了一个简单示例。
⛓️ 链式调用
then
链式调用是 Promise 的核心之一,它通过对Promise.prototype.then
方法的设计,巧妙地解决了传统回调函数中的回调地狱问题。通过then
方法可以将多个Promise对象连接在一起,形成一个调用链,以一种更加清晰的方式编写和组织代码,并使得异步操作的后续任务能够按照既定的逻辑和顺序执行。
那么Promise是如何做到链式调用的呢?以下为then
方法的主要特点,这些特点使得Promise具有链式调用的能力。
方法接收两个参数,分别为当前Promise对象成功后的回调和失败后的回调,即
onFulfilled
和onRejected
。回调执行的时机为当前Promise对象进入已决阶段,若状态为已兑现则执行
onFulfilled
函数,若状态为已拒绝则执行onRejected
函数。回调执行时,当前Promise对象相关联的数据会被传入,且返回值必定是一个新的Promise对象。新Promise对象的状态和数据取决于后续任务的处理,即回调的执行。
- 前一个Promise对象的状态为挂起,后续任务不会被处理,则新Promise对象的状态为挂起,数据为
undefined
。
前一个Promise对象已决,但无后续任务处理,即传入的实参回调不是函数,则新Promise对象的状态和数据与前一个Promise对象保持一致。
后续任务处理成功,即回调执行无错误。新Promise对象的状态和数据取决于回调的返回值,以下是两种情况。
- 回调的返回值也是一个任务(Promise对象),则新Promise对象的状态和数据与回调返回的Promise对象保持一致。
- 回调的返回值不是一个任务(Promise对象),则新Promise对象的状态为已兑现,数据为返回值。
后续任务处理失败,即回调执行抛出错误,则新Promise对象的状态为已拒绝,数据为异常对象。
- 前一个Promise对象的状态为挂起,后续任务不会被处理,则新Promise对象的状态为挂起,数据为
catch
Promise.prototype.catch
方法用于注册一个在Promise对象失败时调用的函数,语法如下。
// 等同于then(null,onRejected)
catch(onRejected)
finally
13123123131231312
🔌 静态API
快速创建
Promise的静态API提供了一种快速创建已决Promise对象的方式,使得我们可以更加方便地进行链式调用。
- resolve
Promise.resolve
静态方法返回一个已兑现的Promise对象,成功数据为给定的第一个参数。语法如下。
Promise.resolve(data)
tip: 若
data
参数也是一个Promise对象,则直接返回这个Promise对象而不做任何修改(返回值和参数data
都指向同一个Promise对象)。
- reject
Promise.reject()
静态方法返回一个已拒绝的Promise对象,失败原因为给定的第一个参数。语法如下。
Promise.reject(reason)
任务并发
Promise提供了一些静态API来处理异步任务的并发。这些方法都接收一个Promise类型的可迭代对象(比如数组或部分伪数组),并返回一个全新的Promise对象。在处理可迭代对象时,其中非Promise类型的元素将使用Promise.resolve
方法自动包装为Promise对象。
tip: JavaScript本质上是单线程的,因此在任何时刻,只会有一个任务执行。Promise的静态API只是让各个任务的执行看起来是并发的。
- all
Promise.all
静态方法的作用为:当所有输入的Promise对象都成功完成时,新Promise对象将被兑现,数据为一个包含所有Promise对象成功数据且顺序一致的数组;而在任意一个输入的Promise对象失败时,新Promise对象将被拒绝,数据为第一个Promise对象失败时的原因。
tip: 若迭代器中没有元素,新Promie对象的状态为已兑现,数据为空数组
[]
。
- any
Promise.any
静态方法的作用为:当所有输入的Promise对象都失败时,新Promise对象将被拒绝,数据为一个包含所有Promise对象失败原因的AggregateError
拒绝对象;而在任意一个输入的Promise对象成功时,新Promise对象将被兑现,数据为第一个Promise对象成功后的数据。
tip:
- 若迭代器中没有元素,则新Promie对象的状态为已拒绝,数据为一个空的
AggregateError
对象。AggregateError
对象拥有一个errors
属性,访问该属性能够获取到一个包含所有Promise对象失败原因的数组。
- allSettled
Promise.allSettled
静态方法的作用为:当所有输入的Promise对象都进入已决阶段时,新Promise对象将被兑现,数据为一个包含所有Promise对象异步处理结果的对象数组,示例如下图所示。
tip: 若迭代器中没有元素,则新Promise对象的状态为已兑现,数据为空数组
[]
。
- race
Promise.race
静态方法的作用为:当输入中的一个Promise对象最先进入已决阶段时,新Promise对象也将进入已决阶段,状态数据与其保持一致。
tip: 若迭代器中没有元素,则新Promise对象的状态为挂起,且永远不会改变。
🔑 async与await
为啥呢?