async 和 await的用法

news2024/9/28 5:26:02

async 函数

async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction构造函数的实例,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promis的异步行为,而无需刻意地链式调用 promise

async 函数还可以被作为表达式来定义。

返回值

一个 Promise,这个 promise 要么会通过一个由 async 函数返回的值被解决,要么会通过一个从 async 函数中抛出的(或其中没有被捕获到的)异常被拒绝。

描述

async 函数可能包含 0 个或者多个 await 表达式。await 表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作被兑现或被拒绝之后才会恢复进程。promise 的解决值会被当作该 await 表达式的返回值。使用 async/await 关键字就可以在异步代码中使用普通的 try/catch 代码块。

备注: await关键字只在 async 函数内有效。如果你在 async 函数体之外使用它,就会抛出语法错误 。

备注: async/await的目的为了简化使用基于 promise 的 API 时所需的语法。async/await 的行为就好像搭配使用了生成器和 promise。

async 函数一定会返回一个 promise 对象。如果一个 async 函数的返回值看起来不是 promise,那么它将会被隐式地包装在一个 promise 中。

例如,如下代码:

async function foo() {
  return 1;
}

等价于:

function foo() {
  return Promise.resolve(1);
}

async 函数的函数体可以被看作是由 0 个或者多个 await 表达式分割开来的。从第一行代码直到(并包括)第一个 await 表达式(如果有的话)都是同步运行的。这样的话,一个不含 await 表达式的 async 函数是会同步运行的。然而,如果函数体内有一个 await 表达式,async 函数就一定会异步执行。

例如:

async function foo() {
  await 1;
}

等价于

function foo() {
  return Promise.resolve(1).then(() => undefined);
}

在 await 表达式之后的代码可以被认为是存在在链式调用的 then 回调中,多个 await 表达式都将加入链式调用的 then 回调中,返回值将作为最后一个 then 回调的返回值。

在接下来的例子中,我们将使用 await 执行两次 promise,整个 foo 函数的执行将会被分为三个阶段。

  1. foo 函数的第一行将会同步执行,await 将会等待 promise 的结束。然后暂停通过 foo 的进程,并将控制权交还给调用 foo 的函数。
  2. 一段时间后,当第一个 promise 完结的时候,控制权将重新回到 foo 函数内。示例中将会将1(promise 状态为 fulfilled)作为结果返回给 await 表达式的左边即 result1。接下来函数会继续进行,到达第二个 await 区域,此时 foo 函数的进程将再次被暂停。
  3. 一段时间后,同样当第二个 promise 完结的时候,result2 将被赋值为 2,之后函数将会正常同步执行,将默认返回undefined 。
async function foo() {
  const result1 = await new Promise((resolve) =>
    setTimeout(() => resolve("1")),
  );
  const result2 = await new Promise((resolve) =>
    setTimeout(() => resolve("2")),
  );
}
foo();

注意:promise 链不是一次就构建好的,相反,promise 链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。

例如,在下面代码中,即使在 promise 链中进一步配置了 .catch 方法处理,也会抛出一个未处理的 promise 被拒绝的错误。这是因为 p2 直到控制从 p1 返回后才会连接到 promise 链。

async function foo() {
  const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
  const p2 = new Promise((_, reject) => setTimeout(() => reject("2"), 500));
  const results = [await p1, await p2]; // 不推荐使用这种方式,请使用 Promise.all 或者 Promise.allSettled
}
foo().catch(() => {}); // 捕捉所有的错误...

示例

简单例子

function resolveAfter2Seconds() {
  console.log("starting slow promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("slow promise is done");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("starting fast promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("fast promise is done");
    }, 1000);
  });
}

async function sequentialStart() {
  console.log("==SEQUENTIAL START==");

  // 1. Execution gets here almost instantly
  const slow = await resolveAfter2Seconds();
  console.log(slow); // 2. this runs 2 seconds after 1.

  const fast = await resolveAfter1Second();
  console.log(fast); // 3. this runs 3 seconds after 1.
}

async function concurrentStart() {
  console.log("==CONCURRENT START with await==");
  const slow = resolveAfter2Seconds(); // starts timer immediately
  const fast = resolveAfter1Second(); // starts timer immediately

  // 1. Execution gets here almost instantly
  console.log(await slow); // 2. this runs 2 seconds after 1.
  console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}

function concurrentPromise() {
  console.log("==CONCURRENT START with Promise.all==");
  return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then(
    (messages) => {
      console.log(messages[0]); // slow
      console.log(messages[1]); // fast
    },
  );
}

async function parallel() {
  console.log("==PARALLEL with await Promise.all==");

  // Start 2 "jobs" in parallel and wait for both of them to complete
  await Promise.all([
    (async () => console.log(await resolveAfter2Seconds()))(),
    (async () => console.log(await resolveAfter1Second()))(),
  ]);
}

sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"

// wait above to finish
setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"

// wait again
setTimeout(concurrentPromise, 7000); // same as concurrentStart

// wait again
setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"

注:

  function test() {
    return "test";
  }
  console.log(test());

等价于

  function test() {
    return "test";
  }
  const msg = test();
  console.log(msg);

输出

 

await 和并行

在 sequentialStart 中,程序在第一个 await 停留了 2 秒,然后又在第二个 await 停留了 1 秒。直到第一个计时器结束后,第二个计时器才被创建。程序需要 3 秒执行完毕。

在 concurrentStart 中,两个计时器被同时创建,然后执行 await。这两个计时器同时运行,这意味着程序完成运行只需要 2 秒,而不是 3 秒,即最慢的计时器的时间。

但是 await 仍旧是顺序执行的,第二个 await 还是得等待第一个执行完。在这个例子中,这使得先运行结束的输出出现在最慢的输出之后。

如果你希望并行执行两个或更多的任务,你必须像在parallel中一样使用await Promise.all([job1(), job2()])

async/await 和 Promise/then 对比以及错误处理

大多数 async 函数也可以使用 Promises 编写。但是,在错误处理方面,async 函数更容易捕获异常错误

上面例子中的concurrentStart函数和concurrentPromise函数在功能上都是等效的。在concurrentStart函数中,如果任一awaited 调用失败,它将自动捕获异常,async 函数执行中断,并通过隐式返回 Promise 将错误传递给调用者。

在 Promise 例子中这种情况同样会发生,该函数必须负责返回一个捕获函数完成的Promise。在concurrentPromise函数中,这意味着它从Promise.all([]).then()返回一个 Promise。

但是,async 函数仍有可能错误地忽略错误。以 parallel async 函数为例。如果它没有等待 await(或返回)Promise.all([]) 调用的结果,则不会传播任何错误。虽然 parallelPromise 函数示例看起来很简单,但它根本不会处理错误!这样做需要一个类似于 return Promise.all([]) 处理方式。

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

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

相关文章

MySQL实用命令

一、 DISTINCT 去重 案例:user_test表对班级字段进行去重操作 SELECT DISTINCT class FROM user_test 二、 的作用 案例: SELECT NULL10 三、 concat 实现连接字符串 ​​​案例: SELECT CONCAT(NAME,class) AS 姓名班级 FROM user_test 四…

看了那场直播后,我们发起了一个讨论

几天前,我们做了一场直播,关于如何用 Serverless 技术让文化古籍“活过来”。 完整视频进入阿里云云原生视频号看直播回放 背景信息: 通过阿里云函数计算帮助复旦大学特藏中心建立数字图书馆,为用户提供更丰富、更具互动性的古籍浏…

使用Claude为杜甫写简历

杜甫生平的百度链接:杜甫(唐代著名现实主义诗人)_百度百科 选中所有内容。 打开claude网页:App unavailable \ Anthropic 粘贴刚刚复制的内容。 输入: 按照这个格式总结杜甫的一生:“公元712年 杜甫出…

Shell脚本学习-while循环2

案例:使用while守护进程的方式监控网站,每隔10秒确定一次网站是否正常。 [rootvm1 scripts]# cat url_monitor1.sh #!/bin/bashif [ $# -ne 1 ]thenecho "USAGE: $0 url"exit 1 fiwhile true doif [ curl -I -s -w "%{http_code}" -…

友盟+、GrowingIO和神策数据 对比

对于市面上的数据平台,先简单归个类。 1、移动统计平台,如友盟、talkingdata、百度云统计、腾讯移动应用统计等。 相同点是数据源都是埋点数据。友盟有免费版本。 前端效果:展现形式上为BI报表。 常用操作是页面内点击和筛选。 使用要求…

使用 RSA 密钥进行 SSH 连接

使用 RSA 密钥进行 SSH 连接 平时用 SSH 连树莓派和虚拟机每次都要输入密码,比较烦人。Windows Terminal 出于安全原因是不支持记录密码进行自动连接的功能的,所以还是老老实实创建 RSA 密钥进行连接好了。 生成 RSA 密钥对 Windows 上可以用 Putty K…

leetcode17. 电话号码的字母组合(java)

电话号码的字母组合 leetcode17. 电话号码的字母组合题目描述回溯算法代码演示 回溯算法 leetcode17. 电话号码的字母组合 难度 中等 leetcode17 跳转链接 题目描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数…

基于遗传算法的试题组卷(二)

实例讲解 一、准备工作 1、问题实体 问题实体包含编号、类型(类型即题型,分为五种:单选,多选,判断,填空,问答, 分别用1、2、3、4、5表示)、分数、难度系数、知识点。一…

前端Vue入门-day08-vant组件库

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 vant 组件库 安装 导入 全部导入 按需导入 浏览器配饰 Viewport 布局 Rem 布局适配 vant 组件库 …

九耶|阁瑞钛伦特:产品经理面试题—产品经理在工作中是如何划分需求优先级的?

产品经理在工作中划分需求优先级是为了指导产品团队的开发和发布流程。以下是产品经理在划分需求优先级时通常考虑的因素: 业务目标:产品经理会与企业领导层或业务方合作,了解公司的战略目标和销售策略。然后,他们会根据这些目标评…

【数据分享】1901-2022年1km分辨率的逐年平均气温栅格数据(免费获取)

气温数据是我们最常用的气象指标之一,之前我们给大家分享过1901-2022年1km分辨率逐月平均气温栅格数据,该数据来源于国家青藏高原科学数据中心,数据持续更新,最新数据为2022年的数据!很多小伙伴拿到数据后还咨询我们有…

Docker-Compose编排与部署(lnmp实例)

第四阶段 时 间:2023年8月3日 参加人:全班人员 内 容: Docker-Compose编排与部署 目录 一、Docker Compose (一)概述 (二)Compose适用于所有环境: (三&#xf…

数据结构基础4:带头指针双向。

一.基本概念: 一.基本结构: 1.逻辑结构: 二.结构体的基本组成。 和单链表非常大的区别就是函数不需要传二级指针,因为头不存有效数据。头节点不需要改变。 二.功能接口的实现 0.创建一个新的节点 //创建新的节点ListNode* buy…

【C++】BSTree 模拟笔记

文章目录 概念插入和删除非递归实现中的问题递归中的引用简化相关OJ复习直达 概念 由下面二叉搜索树的性质可以知道,中序遍历它便可以得到一个升序序列,查找效率高,小于往左找,大于往右走。最多查找高度次,走到到空&am…

黑马头条 各种踩坑合集 从0到1 欢迎留言提问

《黑马头条》SpringBootSpringCloud Nacos等企业级微服务架构项目_黑马头条项目_软工菜鸡的博客-CSDN博客 Day01 用他的 centos镜像 自动集成了 nacos 所有的配置 大部分都要改mysql密码啥的; 启动他的项目,有些时候要启动 redis 容器;镜像里…

超详细|ChatGPT论文润色教程

本文讲述使用中科大开源ChatGPT论文辅助工具,对论文进行润色 祝看到本教程的小伙伴们都完成论文,顺利毕业。 可以加QQ群交流,一群: 123589938 第一章 介绍 今天给大家分享一款非常不错的ChatGPT论文辅助工具,使用了专…

谁是全球最小ARM MCU?

先是从百度上查找的,找了半天,也找到了一部分。 大小基本在一二毫米左右,外设有多有少,但是好多都买不到货。 而且有些特别小众,开发环境压根没接触过。 然后去外面找了一下,竟然有好几个提到了HC32L110&am…

Ubuntu20.04安装踩坑记录---千万不要随便使用sudo rm -rf命令!!!

趁着从服务器下载之前备份内容的这段时间,写一下这阶段安装系统和配置深度学习环境的踩坑路程。作为一周之内重装7次系统的我是懂得怎么把系统搞崩溃的,这不必须记录一下!!! 系统 ubuntu20.04 硬件 dell…

(树) 剑指 Offer 33. 二叉搜索树的后序遍历序列 ——【Leetcode每日一题】

❓剑指 Offer 33. 二叉搜索树的后序遍历序列 难度:中等 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树&#xff1…

调用feign返回错误的数据

bug描述&#xff1a; 在一个请求方法中会调用到feign去获取其他的数据。 List<Demo> list aaaFeignApi.getData(personSelectGetParam);在调用的时候&#xff0c;打断点到feign的地方&#xff0c;数据是存在的&#xff0c;并且有15条。但是返回到上面代码的时候数据就…