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 捕获处理。

输出:


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

相关推荐
旧味清欢|10 分钟前
关注分离(Separation of Concerns)在前端开发中的实践演进:从 XMLHttpRequest 到 Fetch API
javascript·http·es6
热爱编程的小曾28 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin39 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
-代号95271 小时前
【JavaScript】十四、轮播图
javascript·css·css3
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX187302 小时前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员