Promise 详解(上)之 使用篇

基本概念

Promise是异步编程的一种解决方案,被用于表示一个异步操作的最终完成 (成功或失败),及其结果值。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象,得回调变得灵活易懂,也解决了异步回调地狱的问题。

说人话,就是 Promise 一般用来包裹异步任务,等待任务完成的结果,并返回其结果的成功和失败两种情况的回调,可以在相应结果的回调中作出对应的处理。

注意:考虑到篇幅和阅读体验,全文分上下两篇:使用篇、原理篇。本文只涉及到使用部分,下篇会详解原理,几个关键问题,并尝试手撸一份 MyPromise

状态

Promise有三种状态:

  • pending:初始化的状态
  • fulfilled:成功的状态
  • rejected:失败的状态

状态只能由 pending -> fulfilled 或者是 pending -> rejected 两种情况,且状态变化只可能发生一次。

初体验

创建一个Promise对象(起始默认为pending状态):

const p = new Promise(executor)

如:

其中:

  • executor: 执行函数 (resolve, reject) => {}
  • resolve : 定义成功时我们调用的函数 value => {}
  • reject : 定义失败时我们调用的函数 reason => {}

注意:executor 会在 Promise 内部立即同步调用,异步操作在执行函数中执行。

场景:AJAX请求

发起一个常见的一个ajax请求:

控制台输出:

css 复制代码
 {
   message: "https://images.dog.ceo/breeds/dhole/n02115913_4117.jpg"
   status: "success"
 }

then()

then 方法上文也使用到了,有两个可选参数,分别是成功和失败的回调函数:

  • resolved:成功状态,执行第一个回调函数
  • rejected:失败状态,执行第二个回调函数
结果 代码 输出
成功 success:ok
失败 failure:error

并且默认返回一个新的Promise对象,新Promise的结果状态由then()指定的回调函数执行的结果决定的。

情况1:正常情况

如上调用resolve()reject() 两种情况,then() 中的成功和失败的回调函数中正常处理逻辑,并没有显示的 returnthrow抛出错误。

此时的返回新的Promise 对象的状态就是 fulfilled(成功):

情况2:返回失败

如上,只要在then()的任意一个回调函数中抛出一个错误。

此时的返回新的Promise 对象的状态就是 rejected(失败):

情况3:返回非 Promise 的任意对象

如上,在then()的任意一个回调函数中显示得 return 一个任意值。

此时的返回新的Promise 对象的状态就是 fulfilled(成功):

情况4:返回 Promise 对象

返回不同状态的Promise 对象 代码 输出
成功的 Promise 对象
失败的 Promise 对象

如上,在then()的任意一个回调函数中显示得 return 一个Promise 对象。这个Promise 对象的结果就会成为默认返回的新Promise的结果。

catch()

用来指定失败的回调函数。上述说过then()中的两个回调函数都是可选的,可以使用 catch()来只处理失败的回调。

当存在链式调用多个执行时,不需要每一次都写失败回调,可以统一到最后使用 catch()。若有一个Promise的状态为reject的话,则会将异常一直向下穿透直到catch()回调。

如下:

输出: error

链式调用

上面说到的then()会默认返回一个新的Promise对象,因此就可以方便地进行链式调用。

如上,调用了两次then(),按照上述的情况,在then()中处理不同的回调函数,前一个回调函数中的返回值作为后一个回调函数的参数。

finally()

finally 是在ES9(ES2018)中新增的一个特性:表示无论Promise对象变成 fufilled 还是rejected 状态,最终都会被执行。除非是开发者显式终止了链式调用,这个下文会讲到。

finally 方法中的回调函数是不接受参数的,因为无论前面是 fulfilled 状态还是 rejected 状态,它都是执行。

如下:

输出:

静态方法

提供了以下的常用静态方法

Promise.resolve()

将一个普通值转为Promise类型的数据。

参数 代码 输出
非promise对象
Promise对象

结论:

  • 若参数为非 Promise 对象,则返回的结果为成功状态的 Promise 对象
  • 若参数为 Promise 对象,参数的状态决定返回结果的状态

Promise.reject()

用法同上,将一个普通值转为Promise类型的数据。返回的结果始终为失败的回调函数。

输出:

Promise.all()

  • 针对于多个 Promise 的异步任务进行处理
  • 参数:Promise 数组
  • 返回值:Promise对象,状态由数组中的对象状态决定
    • 若每个对象状态都成功,则返回的 Promise 对象的状态为成功,且成功的结果值为每个Promise对象成功结构值组成的数组。
    • 若其中一个对象状态为失败,则返回的 Promise 对象的状态为失败,且失败的结果值为失败的Promise对象的结果值。

简单来说就是有两种情况,类似逻辑中 & 的关系,必须全部成功才返回成功,只要一个失败则返回失败,

情况 代码 输出
全部成功
存在失败

Promise.allSettled()

用来确定要一组异步操作是否都结束了(不管成功或失败)。

  • 参数:Promise 数组
  • 返回值:成功的Promise对象,结果值是由每个 Promise 对象的状态和结果值组成对象的数组

用法跟 Promise.all() 相似,如下:

输出:

Promise.race()

race 有比赛、赛跑的意思,即接受一个Promise对象的数组,比赛谁的状态最新改变。

  • 参数:Promise 数组
  • 返回值:Promise对象,其对象和结果值均由最先改变状态的 Promise 对象决定

如下:

输出:

使用了 setTimeout(是宏任务,不严谨) 模拟了异步任务,看结果果然 为爱冲锋是最快的啊! 。与 Promise.all 相似的是,Promise.race 都是以一个 Promise 对象组成的数组作为参数。不同的是,只要当数组中的其中一个 Promsie 状态变成 resolved 或者 rejected 时,就可以调用then()

Promise.any()

  • 参数:Promise 数组
  • 返回值:Promise对象,状态存在两种情况
    • 数组中只要有一个 Promise 对象为成功状态,则返回的Promise对象的状态为成功,结果值为最先状态变为成功的对象的结果值。
    • 若数组中所有对象都是失败状态,则返回的Promise对象的状态为失败。

简单来说,就是类似逻辑运算中 | 的关系,只要有一个成功则成功;必须全部失败才失败,且不会因为某个对象变成 rejected 状态而结束,必须等到所有的对象状态均变为失败。

成功的情况:

输出:

失败的情况:

输出:

终止 Promise 链条

在链式调用的时候,如果想要在某一个链条终止整个调用,有且只有一种方式:返回一个 pending 状态的 Promise 对象

如下:

输出:

很好理解,返回了一个pending 状态的 Promise 对象,状态没有改变就不会触发后续的回调,且 finally() 方法也不会执行。

async / await

async / await 是ES7提出的基于Promise的解决异步的最终方案。

async

async 是一个加在函数前的修饰符,被 async 定义的函数会默认返回一个 Promise 对象 resolve 的值,即始终是成功状态 (特例就是当返回一个 pending 状态的 Promise 对象时) 。所以对 async 函数可以直接使用 then(),返回值就是then()传入的函数。

基础使用如下:

输出:

上文函数中返回有3种情况,非Promise对象、成功的Promise 、失败的reject。调用后默认返回均是成功状态的 Promise。

特例:若返回了一个pending 状态的 Promise 对象,则调用后默认返回就是pending 状态的 Promise 对象,且后续的链式回调不再执行。

await

await 也是一个修饰符,只能放在 async 定义的函数内。

  • 修饰 Promise 对象,可以获取 Promise 中返回的内容,即 resolve()reject() 的参数,且取到值后语句才会往下执行,即同步执行。
  • 修饰非 Promise 对象:把这个非 Promise 的东西当做 await 表达式的结果。
  • await 必须写在 async 函数中,但是 async 函数中可以没有 await 。
  • 如果 await 的 Promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理。

输出:


个人的学习总结,有误的地方欢迎指正!

相关推荐
小牛itbull10 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i19 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_21 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
guokanglun27 分钟前
空间数据存储格式GeoJSON
前端
GIS瞧葩菜31 分钟前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium
ZBY520311 小时前
【Vue】 npm install amap-js-api-loader指南
javascript·vue.js·npm
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
brief of gali1 小时前
记录一个奇怪的前端布局现象
前端
前端拾光者2 小时前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化