【javascript】Promise的介绍和使用方法

news2024/11/26 19:47:01

简言

什么是Promise?
Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。它可以进行链式调用、错误捕捉等操作,轻松解决经典的回调地狱问题,可以像编写同步代码那样,从容、简洁、优雅地实现高复杂度的异步函数组合调用代码。

Promise

Promise 对象表示异步操作最终的完成(或失败)以及其结果值。

Promise描述

一个 Promise 是一个代理,它代表一个在创建 promise 时的不确定的值。 它使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。

一个Promise有以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝,类似于等待结果值状态。
  • 已兑现(fulfilled):意味着操作成功完成,已知道结果值。
  • 已拒绝(rejected):意味着操作失败状态。

一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用。

如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。你还会听到使用已解决(resolved)这个术语来描述 Promise——这意味着该 Promise 已经敲定(settled),或为了匹配另一个 Promise 的最终状态而被“锁定(lock-in)”,进一步解决或拒绝它都没有影响。
mdn上的图,从左往右看:
在这里插入图片描述

Promise使用

promise使用比较方便,记住链式调用即可,它有多个方法供不同场景使用,大多方法返回的值是promise对象。

Promise构造函数

Promise() 构造函数创建 Promise 对象。
语法:
new Promise(executor)

-executor — 是一个函数,它接收两个函数作为参数:resolveFunc(抛出结果函数) 和 rejectFunc(抛出失败函数)。

当通过 new 关键字调用 Promise 构造函数时,它会返回一个 Promise 对象。当 resolveFunc 或者 rejectFunc 被调用时,该 Promise 对象就会变为已解决(resolved)
示例:

const pro = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});
console.log(pro, Object.prototype.toString.call(pro));

pro.then(() => {
  console.log(pro);
});

在这里插入图片描述
以下是典型的 Promise 流程概述:

  1. 在构造函数生成新的 Promise 对象时,它还会生成一对相应的 resolveFunc 和 rejectFunc 函数;它们与 Promise 对象“绑定”在一起。

  2. executor 通常会封装某些提供基于回调的 API 的异步操作。回调函数(传给原始回调 API 的函数)在 executor 代码中定义,因此它可以访问 resolveFunc 和 rejectFunc。

  3. executor 是同步调用的(在构造 Promise 时立即调用),并将 resolveFunc 和 rejectFunc 函数作为传入参数。

  4. executor 中的代码有机会执行某些操作。异步任务的最终完成通过 resolveFunc 或 rejectFunc 引起的副作用与 Promise 实例进行通信。这个副作用让 Promise 对象变为“已解决”状态。

    • 如果先调用 resolveFunc,则传入的值将解决。Promise 可能会保持待定状态(如果传入了另一个 thenable 对象),变为已兑现状态(在传入非 thenable 值的大多数情况下),或者变为已拒绝状态(在解析值无效的情况下)。

    thenable对象就是实现Thenable接口(处理异步操作)的对象,Promise对象也是thenable对象。

    • 如果先调用 rejectFunc,则 Promise 立即变为已拒绝状态。
    • 一旦 resolveFunc 或 rejectFunc 中的一个被调用,Promise 将保持解决状态。只有第一次调用 resolveFunc 或 rejectFunc 会影响 Promise 的最终状态,随后对任一函数的调用都不能更改兑现值或拒绝原因,也不能将其最终状态从“已兑现”转换为“已拒绝”或相反。
    • 如果 executor 抛出错误,则 Promise 被拒绝。但是,如果 resolveFunc 或 rejectFunc 中的一个已经被调用(因此 Promise 已经被解决),则忽略该错误。
    • 解决 Promise 不一定会导致 Promise 变为已兑现或已拒绝(即已敲定)。Promise 可能仍处于待定状态,因为它可能是用另一个 thenable 对象解决的,但它的最终状态将与已解决的 thenable 对象一致。
  5. 一旦 Promise 敲定,它会(异步地)调用任何通过 then()、catch() 或 finally() 关联的进一步处理程序。最终的兑现值或拒绝原因在调用时作为输入参数传给兑现和拒绝处理程序


console.log(
  new Promise((resolve) => {
    resolve(new Error("error"));
  })
);
console.log(
  new Promise((resolve, reject) => {
    reject("reject状态");
  })
);

Promise链式调用(then函数)

Promise 实例的 then() 方法最多接受两个参数:用于 Promise 兑现和拒绝情况的回调函数。它立即返回一个等效的 Promise 对象,允许你链接到其他 Promise 方法,从而实现链式调用。
then() 函数会返回一个和原来不同的新的 Promise:

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

promise2 不仅表示 doSomething() 函数的完成,也代表了你传入的 successCallback 或者 failureCallback 的完成,这两个函数也可以返回一个 Promise 对象,从而形成另一个异步操作,这样的话,在 promise2 上新增的回调函数会排在这个 Promise 对象的后面。
就像这样,每一个 Promise 都代表了链中另一个异步过程的完成。

then 的参数是可选的,catch(failureCallback) 等同于 then(null, failureCallback)

示例:

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`得到最终结果:${finalResult}`);
  })
  .catch(failureCallback);

注意:一定要有返回值,否则,回调将无法获取上一个 Promise 的结果。

Catch 的后续链式操作
有可能会在一个回调失败之后继续使用链式操作,即,使用一个 catch,这对于在链式操作中抛出一个失败之后,再次进行新的操作会很有用(上面提示了,catch相当于拒绝状态的then)。

new Promise((resolve) => {
  resolve(123);
})
  .then((res) => {
    console.log(res, "then");
    //  抛错,触发catch
    throw new Error(res);
  })
  .catch((res) => {
    console.log(res, "catch");
    return "catch返回结果";
  })
  .then((err) => {
    console.log(err, "catch后的then");
  });

在这里插入图片描述

Promise错误捕捉(catch)

Promise 实例的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。

new Promise((resolve, reject) => {
  reject("错误啦");
}).catch((res) => {
  console.log(res, "catch");
});
new Promise((resolve, reject) => {
  reject("错误啦2");
});

注意点:

  • 捕获不到异步函数内部抛出的错误。
  • 在调用 resolve 之后抛出的错误会被忽略。
  • 如果 Promise 已兑现,catch() 不会被调用,例如Promise.resolve()返回的promise对象。

finally函数

**Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。**它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法。
如果你想在 promise 敲定时进行一些处理或者清理,无论其结果如何,那么 finally() 方法会很有用。
finally() 方法类似于调用 then(onFinally, onFinally)。然而,有几个不同之处:

  • 创建内联函数时,你可以只将其传入一次,而不是强制声明两次或为其创建变量。
  • onFinally 回调函数不接收任何参数。这种情况恰好适用于你不关心拒绝原因或兑现值的情况,因此无需提供它。
  • finally() 调用通常是透明的,不会更改原始 promise 的状态。例如 Promise.resolve(2).finally(() => 77) 返回一个最终兑现为值 2 的 promise。

在 finally 回调函数中强制抛出拒绝结果或错误),会导致promise状态变成拒绝状态。

console.log(
  new Promise((resolve, reject) => {
    reject("错误啦2");
  }).finally(() => {
    console.log("finally");
    return Promise.reject("finally1");
  })
);
console.log(
  new Promise((resolve, reject) => {
    resolve("success");
  }).finally(() => {
    console.log("finally");
    return Promise.reject("finally2");
  })
);

在这里插入图片描述

Promise.resolve()

Promise.resolve() 静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。

该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。

Promise.resolve() 返回一个 Promise 对象,其最终状态取决于另一个 Promise 对象、thenable 对象或其他值

Promise.resolve("成功").then(
  (value) => {
    console.log(value); // "成功"
  },
  (reason) => {
    // 不会被调用
  },
);

Promise.resolve() 方法会重用已存在的 Promise 实例。如果它正在解决一个原生的 Promise,它将返回同一 Promise 实例,而不会创建一个封装对象。

const original = Promise.resolve(33);
const cast = Promise.resolve(original);
cast.then((value) => {
  console.log(`值:${value}`);
});
console.log(`original === cast ? ${original === cast}`);

// 按顺序打印:
// original === cast ? true
// 值:33

Promise.reject()

Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。
Promise.reject(reason)
Promise.reject 静态方法返回一个被拒绝的 Promise 对象。通过使用 Error 的实例获取错误原因 reason 对调试和选择性错误捕捉很有帮助。

const p = Promise.resolve(1);
const rejected = Promise.reject(p);
console.log(rejected === p); // false
rejected.catch((v) => {
  console.log(v === p); // true
});

与 Promise.resolve 不同,Promise.reject 方法不会重用已存在的 Promise 实例。

Promise组合使用方法

Promise有多个静态方法用于快速处理promise可迭代对象。

  • Promise.all() — Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。理想情况下是返回promise结果数组,Promise.all() 方法会在任何一个输入的 Promise 被拒绝时立即拒绝

    Promise.all() 方法是 promise 并发方法之一。它可用于聚合多个 Promise 的结果。通常在有多个相关的异步任务并且整个代码依赖于这些任务成功完成时使用,我们希望在代码执行继续之前完成所有任务。

  • Promise.allSettled() — Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。也是返回结果数组,不过即使其中有失败也不会阻止其他promise

    Promise.allSettled() 方法是 promise 并发方法之一。在你有多个不依赖于彼此成功完成的异步任务时,或者你总是想知道每个 promise 的结果时,使用 Promise.allSettled() 。

  • Promise.any() — Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。只要有一个欧容米色成功就返回第一个成功的promise值,都拒绝则返回拒绝原因数组

    Promise.any() 方法是 Promise 并发方法之一。该方法对于返回第一个兑现的 Promise 非常有用。一旦有一个 Promise 兑现,它就会立即返回,因此不会等待其他 Promise 完成。

  • Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。

    Promise.race() 方法是 Promise 并发方法之一。当你想要第一个异步任务完成时,但不关心它的最终状态(即它既可以成功也可以失败)时,它就非常有用。

四个组合方法两两相似,all和allSettled,any和race,建议对比记忆。

Promise.all 的快速失败行为.
Promise.all 在任意一个传入的 promise 失败时返回失败。例如,如果你传入四个超时后解决的 promise 和一个立即拒绝的 promise,那么 Promise.all 将立即被拒绝。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("一"), 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("二"), 2000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("三"), 3000);
});
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("四"), 4000);
});
const p5 = new Promise((resolve, reject) => {
  reject(new Error("拒绝"));
});

// 使用 .catch:
Promise.all([p1, p2, p3, p4, p5])
  .then((values) => {
    console.log(values);
  })
  .catch((error) => {
    console.error(error.message);
  });

// 打印:
// "拒绝"

使用 Promise.allSettled():

Promise.allSettled([
  Promise.resolve(33),
  new Promise((resolve) => setTimeout(() => resolve(66), 0)),
  99,
  Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));

// [
//   { status: 'fulfilled', value: 33 },
//   { status: 'fulfilled', value: 66 },
//   { status: 'fulfilled', value: 99 },
//   { status: 'rejected', reason: Error: 一个错误 }
// ]

使用 Promise.any()显示第一张已加载的图片:

async function fetchAndDecode(url, description) {
  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(`HTTP 错误!状态码:${res.status}`);
  }
  const data = await res.blob();
  return [data, description];
}

const coffee = fetchAndDecode("coffee.jpg", "Coffee");
const tea = fetchAndDecode("tea.jpg", "Tea");

Promise.any([coffee, tea])
  .then(([blob, description]) => {
    const objectURL = URL.createObjectURL(blob);
    const image = document.createElement("img");
    image.src = objectURL;
    image.alt = description;
    document.body.appendChild(image);
  })
  .catch((e) => {
    console.error(e);
  });

使用 Promise.race() 实现请求超时:

const data = Promise.race([
  fetch("/api"),
  new Promise((resolve, reject) => {
    // 5 秒后拒绝
    setTimeout(() => reject(new Error("请求超时")), 5000);
  }),
])
  .then((res) => res.json())
  .catch((err) => displayError(err));

结语

Promise是微任务的一种。
结束了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1561077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法分析与设计】丑数 | |

📝个人主页:五敷有你 🔥系列专栏:算法分析与设计 ⛺️稳中求进,晒太阳 题目 给你一个整数 n ,请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 示例 1:…

智慧城市数字孪生,综合治理一屏统览

现代城市作为一个复杂系统,牵一发而动全身,城市化进程中产生新的矛盾和社会问题都会影响整个城市系统的正常运转。智慧城市是应对这些问题的策略之一。城市工作要树立系统思维,从构成城市诸多要素、结构、功能等方面入手,系统推进…

Python基础之Class类的定义、继承、多态

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、class类1.类属性操作(增删改)2.类方法操作 二、类的继承1、语法2、方法重写 二、类的多态 一、class类 、三部分组成 1、类名&#xff…

makefile第五讲

更多精彩内容在公众号。 书写命令 每条规则中的命令和操作系统 Shell 的命令行是一致的。 make 会按顺序一条一条的执行命令, 每条命令的开头必须以[Tab]键开头, 除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被…

FreeRTOS系列补充1:重点记忆知识点

1、FreeRTOS的调度策略、内部原理 FreeRTOS采用抢占式优先级调度策略。任务根据其优先级进行调度,优先级高的任务会抢占优先级低的任务的执行权。FreeRTOS使用任务控制块(Task Control Block,简称TCB)来管理任务信息和状态。调度…

好展位、抢先订!2024第19届亚洲泳池SPA展览会|广州游池展

邀请函/2024第19届亚洲泳池SPA博览会 时间:2024年5月10日--12日 地点:广州中国进出口商品交易会展馆 主办单位:广东省沐浴休闲协会 广东省水处理技术协会 广东鸿威国际会展集团有限公司 支持单位:国际桑拿协会 韩国SPA协会 亚洲水疗康体促…

leecode 331 |验证二叉树的前序序列化 | gdb 调试找bug

计算的本质是数据的计算 数据的计算需要采用格式化的存储, 规则的数据结果,可以快速的按照指定要求存储数据 这里就不得不说二叉树了,二叉树应用场景真的很多 本题讲的是,验证二叉树的前序序列化 换言之,不采用建立树的…

monocle2 orderCells报错

问题:运行monocle2(version2.26.0)的orderCells出现以下错误 解决方式:出现错误后,运行以下命令: trace(project2MST, edit T, where asNamespace("monocle")) 然后,在弹出的窗口…

Meta Pixel:助你实现高效地Facebook广告追踪

Meta Pixel 像素代码是用來衡量Facebook广告效果的一个官方数据工具,只要商家有在Facebook上投放广告就需要串联Meta Pixel 像素代码来查看相关数据。 它本质上是一段 JavaScript 代码,安装后可以让用户在自己网站上查看到访客活动。它的工作原理是加载…

如何在CentOS7部署Wiki.js知识库并实现分享好友公网远程使用【内网穿透】

文章目录 1. 安装Docker2. 获取Wiki.js镜像3. 本地服务器打开Wiki.js并添加知识库内容4. 实现公网访问Wiki.js5. 固定Wiki.js公网地址 不管是在企业中还是在自己的个人知识整理上,我们都需要通过某种方式来有条理的组织相应的知识架构,那么一个好的知识整…

小黑逆向爬虫探索与成长之路:小黑独立破解毛毛租数据加密与解密

前言 有道和招标网的加密入口定位在前面两期做了详细的介绍,本小结将通过简单的关键词搜索定位到加密与解密入口 数据接口寻找与请求 根据响应数据长度,确定数据接口,发现传入的参数需要加密,响应的结果需要解密,后…

Python异常处理:基础到进阶的实用指南

前言 大家好,我是海鸽。异常处理在工程文件中必不可少,今天就带大家彻底搞定python的异常处理。 什么是异常 在Python中,异常是指在程序执行过程中出现的错误或异常情况。 当Python解释器无法执行代码时,它会引发异常&#xff0…

Excel 粘贴回筛选后的单元格不能完全粘老是少数据 ,有些单元格还是空的

环境: excel2021 Win10专业版 问题描述: excel 粘贴回筛选后的单元格不能完全粘老是少数据 有些单元格还是空的 复制选择筛选后A1-A10单元格 ,定位条件)(仅可见单元格)来访问,或者你可以使用…

算法沉淀 —— 深度搜索(dfs)

算法沉淀 —— 深度搜索(dfs) 一、计算布尔二叉树的值二、求根节点到叶节点数字之和三、二叉树剪枝四、验证二叉搜索树五、二叉搜索树中第K小的元素 一、计算布尔二叉树的值 【题目链接】:2331. 计算布尔二叉树的值 【题目】: …

MambaMixer、NeSLAM、Talk3D、BundledSLAM、ShapeFusion

本文首发于公众号:机器感知 MambaMixer、NeSLAM、Talk3D、BundledSLAM、ShapeFusion ShapeFusion: A 3D diffusion model for localized shape editing In the realm of 3D computer vision, parametric models have emerged as a ground-breaking methodology fo…

全套医院手术麻醉系统源码 人工智能麻醉系统源码 医疗管理系统源码

全套医院手术麻醉系统源码 人工智能麻醉系统源码 医疗管理系统源码 手术麻醉临床信息系统有着完善的临床业务功能,能够涵盖整个围术期的工作,能够采集、汇总、存储、处理、展现所有的临床诊疗资料。通过该系统的实施,能够规范麻醉科的工作流…

Qt实现通过.css样式文件实时加载QSS样式

初学Qt时要想通过QSS修改控件QWidget,QPushButton等原生基础控件的样式,一般都是直接在.ui文件中直接添加qss,或者在代码中通过setStyleSheet(QString qss)来设置。当程序很大时,很多地方需要复用样式时会非常麻烦,qss…

C++教学——从入门到精通 6.ASCII码与字符型

如何把小写字母转换成大写字母呢? 这个问题问的好,首先我们要新学一个类型——char 这个类型就是字符型 再来说说ASCII码 给大家举几个例子 空格————32 0————48 9————57 A————65 Z————90 a————97 z————122 我们…

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】 题目描述:解题思路一:一边算前缀和一边统计。这里用哈希表统计前缀和出现的次数,那么和为k的子数组的个数就是当前前缀和-k的个数,即preSums[presum - k]。画个图表述就是&a…

NGINX 反向代理 CORS

我遇到了一个问题就是 Nginx 是作为反向代理服务器部署的,但因为 Nginx 的配置导致 CORS 问题。 在这个时候我们可以对 Nginx 的配置文件进行修改: 在 location 后添加下面的内容: add_header Access-Control-Allow-Origin *; add_header A…