异步发展流程 —— 异步编程的终极大招 async/await

深入理解 JavaScript 中的 Async/Await:异步编程的终极解决方案 JavaScript异步编程从回调函数到Promise再到async/await不断演进。async/await作为Promise的语法糖,通过更直观的同步式语法处理异步操作,解决了回调地狱和错误处理难题。本文解析了async/await的基础语法、高级应用及最佳实践。基础部分介绍了async函数自动包装Promise的特性,await的等待机制以及try-catch错误处理。高级应用展示了如何用Promise.all并行执行异步操作,以及在条件语句和循环中的使用技巧。通过对比串行与并行执行的时间差异,凸显 阅读详情

在这里插入图片描述


阅读原文


前言

这篇文章是异步发展流程系列的最后一篇,可能会涉及 Promise、Generators、co 等前置知识,如果对这些不是很了解可以看这个系列的前三篇:

如果已经具备这些前置知识,那我们继续看看今天的主角,JavaScript 异步编程的终极大招 async/await。


async/await 简介

async/await 指的是两个关键字,是 ES7 引入的新标准,async 关键字用于声明 async 函数,await 关键字用来等待异步(必须是 Promise)操作,说白了 async/await 就是 Generators + co 的语法糖。

async/await 和 Generators + co 的写法非常的相似,只是把用于声明 Generator 函数的 * 关键字替换成了 async 并写在了 function 关键字的前面,把 yield 关键字替换成了 await;另外,async 函数是基于 Promise 的,await 关键字后面等待的异步操作必须是一个 Promise 实例,当然也可以是原始类型的值,只不过这时的执行效果等同于同步,与 Generator 不同的是,await 关键字前可以使用变量去接收这个正在等待的 Promise 实例执行后的结果。


async 函数的基本用法

async 函数返回一个 Promise 实例,可以使用 then 方法添加回调函数。当函数执行的时候,只要遇到 await 就会等待,直到 await 后面的同步或异步操作完成,再接着执行函数体内后面的语句。

1、async 函数声明

async 的声明方式大概有以下几种:

//  async 函数声明
// 函数声明
async function fn() {}

// 函数表达式
const fn = async function() {};

// 箭头函数
const fn = async () => {};

// 作为对象的方法
let obj = {
    async fn() {}
};

// 作为 class 的方法
class Person(name) {
    constructor () {
        this.name = name;
    }
    async getName() {
        const name = await this.name;
        return name;
    }
}

在上一篇介绍 Generators + co 的文章中我们举了一个例子,使用 NodeJS 的 fs 模块连续异步读文件,第一个文件名为 a.txt,读到的内容为 b.txt,作为要读的第二个文件的文件名,继续读 b.txt 后将读到的内容 “Hello world” 打印出来。

我们来使用 async/await 的方式来实现一下:

// async 函数实现文件读取
// 引入依赖
const fs = require("fs");
const util = require("util");

// 将 fs.readFile 转换成 Promise
const readFile = util.promisify(fs.readFile);

// 声明 async 函数
async function read(file) {
    let aData = await readFile(file, "utf8");
    let bData = await readFile(aData, "utf8");
    return bData;
}

// 调用 async 函数
read("a.txt").then(data => {
    console.log(data); // Hello world
});

其实对比上一篇文章 Generator 的案例,与 Generator 函数一样,写法像同步,执行是异步,不同的是我们即没有手动调用 next 方法,也没有借助 co 库,其实是 async 函数内部集成了类似于 co 的执行器,帮我们在异步完成后自动向下执行代码,所以说 async/await 是 Generators + co 的语法糖。

2、async 函数错误处理

async 函数内部如果执行错误可以有三种方式进行错误处理:

  • 在 await 后面的 Promise 实例使用 then 方法错误的回调或 catch 方法进行错误处理;
  • 如果有多个 await,可以在 async 函数执行完后使用 catch 方法统一处理;
  • 由于 async 内部代码是同步的写法,多个 await 的情况也可以使用 try...catch... 进行处理。

需要注意的是,如果在 async 函数内部使用了 try...catch... 又在函数执行完后使用了 catch,错误会优先被同步的 try...catch... 捕获到,后面的 catch 就不会再捕获了。

// async 函数异常捕获
// 第一种
async function fn() {
    let result = await Promise.reject("error").catch(err => {
        console.log(err);
    });
}

fn(); // error

// 第二种
async function fn() {
    try {
        let val1 = await Promise.reject("error");
        let val2 = await Promise.resolve("success");
    } catch (e) {
        console.log(e);
    }
}

fn(); // error

// 第三种
async function fn() {
    let val1 = await Promise.resolve("success");
    let val2 = await Promise.reject("error");
}

fn().catch((err => console.log(err))); // error

3、await 异步并发

在 async 函数中,如果有多个 await 互不依赖,这种情况下如果执行一个,等待一个完成,再执行一个,再等待完成,这样是很浪费性能的,所以我们要把这些异步操作同时触发。

假设我们异步读取两个文件,且这两个文件不相关,我可以使用下面的方式来实现:

// await 异步并发
// 前置
const fs = require("fs");
const util = require("util");
const readFile = util.promisify(fs.readFile);

// 需要改进的 async 函数
async function fn() {
    let aData = await readFile("a.txt", "utf8");
    let bData = await readFile("b.txt", "utf8");
    return [aData, bData];
}

fn();

// 在 async 函数外部触发异步
let aDataPromise = readFile("a.txt", "utf8");
let bDataPromise = readFile("b.txt", "utf8");

async function fn() {
    let aData = await aDataPromise;
    let bData = await bDataPromise;
    return [aData, bData];
}

fn();

// 使用 Promise.all
async function fn() {
    let dataArr = await Promise.all(
        readFile("a.txt", "utf8"),
        readFile("a.txt", "utf8")
    );
    return dataArr;
}

fn();

4、使用 async/await 的注意点

使用 async/await 应注意以下几点:

  • 对 await 习惯性错误处理;
  • await 命令后互不依赖的异步应同时触发;
  • async 函数中,函数的执行上/下文发生变化时,不能使用 await(如使用 forEach 循环的回调中)。

针对第一点,在 async 函数中 await 命令后面大多情况下是 Promise 异步操作,运行结果可能出现错误并调用 reject 函数,最好对这个 await 语句进行错误处理,具体方式参照 async 函数基本用法中关于错误处理的内容。

针对第二点,如果两个或多个 await 命令后的异步操作没有依赖关系,执行时,需先触发第一个,等待异步完成,再触发第二个,再等异步完成,依次类推,这样比较耗时,性能不好,所以应该将这些异步操作同时触发,触发方式参照 async 函数基本用法中的 await 异步并发的内容。

针对第三点,如果声明一个 async 函数并传入一个数组,数组里面存储的都是 Promise 实例,若使用 forEach 循环数组,由于函数的执行上/下文发生了变化,此时使用 await 命令会报错。

// 循环内使用 await
// 创建 Promise 实例
let p1 = Promise.resolve("p1 success");
let p2 = Promise.resolve("p2 success");
let p3 = Promise.resolve("p3 success");


// async 函数
async function fn(promises) {
    promise.forEach(function (promise) {
        await promise;
    });
}

fn([p1, p2, p3]); // 执行时报错


// 修改方式
async function fn(promises) {
    for(let i = 0; i < promises.length; i++) {
        await pormises[i];
    }
}

fn([p1, p2, p3]); // 正常执行

总结

async/await 的实现原理,其实就是在 async 函数内部逻辑映射成了 Generator 函数并集成了一个类似于 co 的执行器,所以我们使用 async/await 的时候,代码更简洁,没有了自己触发遍历器的 next 或调用 co 充当执行器的过程,只需要关心 async 函数的内部逻辑就可以了,因为写法与同步相同,更提高了代码的可读性,所以说 async/await 是异步编程的终极大招。

由于 async/await 是 ES7 规范,在浏览器端的支持并不是那么的友好,所以现在这种写法多用在 NodeJS 的异步操作当中,在 NodeJS 框架 Koa 2.x 版本得到广泛应用。

最后希望大家在读过异步发展流程这个系列之后,对 JavaScript 异步已经有了较深的认识,并可以在不同情况下游刃有余的使用这些处理异步的编程手段。


C#异步编程 async/await C#的async/await是基于 Task 的语法糖,本质上不引入新的多线程机制,而是简化异步编程模型,让异步代码像同步代码一样简洁易读。async用于修饰方法,await用于等待异步操作,需配合使用。适用于I/O密集型任务(如网络请求、文件操作)和UI交互场景,不适合CPU密集型计算 阅读详情

相关推荐

C# 异步编程async/await

异步这个概念在不同语境下有不同的解释,比如在一个单核CPU里开启两个线程执行两个函数,通常认为这种调用是异步的,但对于CPU来说它是单核不可能同时运行两个函数,不过是由系统调度在不同的时间分片中执行。一般来说,如果两个工作能同时进行,就认为是异步的。在编程中,它通常代表函数的调用可以在不执行完的情况下返回,必要时在完成时回调。有一个概念常常被混淆,多线程和异步。

xxxzzzqqq_的博客 1489

异步编程 - 01 漫谈异步编程发展史

如果使用异步编程 ,则可以在调用线程内开启一个异步运行单元来执行任务A,开启异步运行单元后调用线程会马上返回一个Future对象(futureB),然后调用线程本身来执行任务B,等任务B执行完毕后,调用线程可以调用futureB的get()方法获取任务A的执行结果,最后再拼接两者的结果。由于服务B和服务C是并发运行,所以相比同步调用,线程A获取到服务B和服务C结果的时间会缩短很多(同步调用情况下的耗时为服务B和服务C返回结果耗时的和,异步调用情况下耗时为max(服务B耗时,服务C耗时))。

小工匠 1万+

C# 异步之Async/await

该文章仅为个人理解,如有错误请指正,标红内容为重点,会有针对异步的多个文章,包含Task,APM(BeginInvoke,EndInvoke)等。 一、基本概念: Async/await 关键字是在.Net 4.5引入的关键字来创建自动延续任务实现异步调用。 async/await是语法糖,等价于task.Run().ContinueWith(action,Task.FromCurrentS...

窝。。。 2089

浅析异步的发展过程

异步的发展过程 第一个阶段:回调函数(callback)。一层一层的嵌套,会产生回调地狱,可读性不强,不便于后期维护 第二个阶段:promise。ES6提出的。promise对象,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),用then方法来指定resolved(成功)状态的回调函数,用catch方法指定rejected(失败)状态的回调函数。有两特征:1、状态不受外界影响;2、一旦状态改变,就不会再变,任何时候都可以得到这个结果 第三个阶段:g

m0_52602525的博客 317

.NET异步编程总结----四种实现模式代码总结

本篇文章主要介绍了.NET异步编程总结----四种实现模式,详细的介绍了每种方法的实现和实例,具有一定的参考价值,有兴趣的可以了解一下。

谈谈JavaScript异步函数发展历程

《The Evolution of Asynchronous JavaScript》外文梳理了JavaScript异步函数的发展历程,首先通过回调函数实现异步,之后又经历了Promise/A+、生成器函数,而未来将是async函数的。感谢景庄对该文章的翻译,内容如下: 现在让我们一起来回顾这些年来JavaScript异步函数的发展历程吧。 回调函数Callbacks 似乎一切应该从回调函数开始谈起。 异步JavaScript 正如我们所知道的那样,在JavaScript中,异步编程方式只能通过JavaScript语言中的一等公民函数才能完成:这种方式意味着我们可以将一个函数作为另一个函数的参数

异步的发展历程(小白版)

因为自己淋过雨,所以想给别人撑把伞。在这一节,因为主要目标对象是刚学异步的小白同学,所以笔者打算用更通俗的语言来简单梳理一下这方面的内容。😋最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。

梅花寺 334

用友U8登录慢卡顿?别急着重启服务器,先检查UA_Message这张表(附SQL清理脚本)

本文针对用友U8登录慢卡顿问题,深入分析UA_Message表积压消息对系统性能的影响,并提供SQL清理脚本和优化方案。通过诊断、清理、索引优化和预防措施四步法,显著提升U8客户端登录速度,适用于后台管理和数据库维护场景。

weixin_28726671的博客 380

javascript异步编程发展史之搞懂异步编程

前言 异步编程对于JavaScript语言来说非常重要,由于JavaScript是单线程语言如果没有异步编程,根本无法使用,不然就会卡死,所以理解并学会异步编程尤为重要,本文我会梳理JavaScript异步的发展史,让大家清楚了解每一种异步流程的解决方案,然后推出现在最优的异步编程方案,帮助大家彻底掌握异步在JavaScript的使用,在面试以及以后的工作中才能游刃有余。 1.基础知识掌握 要想学会异步编程,我们必须要对JavaScript写的程序运行方式足够了解,大家需要掌握的知识如下 单线程模型的理解

weixin_56436951的博客 2130

零基础Python实战:手把手教你用PyGame制作动态烟花效果(附完整代码)

本文为零基础Python编程爱好者提供了一份详细的PyGame实战教程,手把手教你从环境搭建到代码实现,逐步构建一个动态烟花模拟器。教程涵盖了粒子系统、物理模拟(重力与阻力)、效果优化(拖尾与爆炸形态)以及完整的交互式主程序,帮助你通过Python烟花代码项目深入理解图形编程与面向对象设计。

weixin_29062255的博客 257

Star CCM+ 实战:旋风分离器网格加密策略与湍流模拟优化

本文针对Star CCM+中旋风分离器模拟精度不足的常见问题,深入剖析了网格质量的关键影响。文章重点介绍了通过局部网格加密策略,精准优化旋流核心区与入口区域的网格密度,从而在保证计算效率的前提下,显著提升湍流模拟的准确性与流场细节的捕捉能力,为工程实践提供了一套有效的优化方案。

water的专栏 535

Rust 异步编程详解(async/await)

Rust异步编程通过async/await语法和Future trait实现高效I/O处理,核心机制包括状态机模型和运行时调度。文章详细解析了异步编程基础、核心组件(Future trait、async/await语法)、工作流程,并提供了HTTP请求等代码示例。同时探讨了错误处理、任务取消、Pin/Unpin等高级话题,比较了不同运行时的适用场景。最佳实践建议选择合适运行时、避免阻塞操作,并指出异步编程在性能和安全方面的优势,以及调试复杂度等限制。附带的并发网络请求示例展示了tokio运行时的实际应用,包

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 2223

随机向量函数链神经网络(RVFLNN)简介——附测试代码

宽度学习(Broad Learning System)原理介绍:随机向量函数链神经网络(RVFLNN) 目录宽度学习(Broad Learning System)原理介绍:随机向量函数链神经网络(RVFLNN)结构分析和训练优化 函数链神经网络(Functional-link neural network,简称FLNN)的结构如下图。 和深度神经网络结构不同的是,FLNN相当于把隐层放到了输入层中,作为增强结点(Enhancement nodes)。也可以理解成FLNN在输入层就对输入向量进行了非线性变换

weixin_44517742的博客 1万+

【异步系列四】async await 原理解析之爱上 async/await

异步编程一直是 JavaScript 中比较麻烦但相当重要的一件事情,一直也有人在提出各种方案,试图解决这个问题。从回调函数到 Promise 对象,再到 Generator 函数,每次都有所改进,但都不彻底,直到出现了 async 函数,很多人认为它是异步操作的终极解决方案。但很多人对于async 和 await 的原理却一知半解,只知道可以解决异步问题,知其然,不知其所以然。所以,本篇文章对于async、await 的原理进行详细解释,希望可以帮到大家。有疑问,欢迎留言评论。

二三的博客 5647

点云欧式聚类快速了解

一种基于欧氏距离度量的聚类算法基于KD-Tree的近邻查询算法是加速欧式聚类算法的重要预处理方法KD-Tree是由二分搜索树演变而来的用于大规模高维度数据查找场景当中的索引结构,主要用于最近邻查找和近似最近邻查找。将整个空间划分为二叉树的形式,方便有效找到最近邻点云处理算法库PCL,是比较重要的,在后续会继续学习,然后发出更优质的文章。

xiaobaiwsc的博客 1722

信奥赛C++提高组csp-s之线段树详解

信奥赛C++提高组csp-s之线段树详解

王老师青少年编程 925

C# 异步编程 async、await

「C#」异步编程玩法笔记-async、await

Raink的博客 6833

第67步 时间序列建模实战:ARIMA建模(Stata)

第67步 时间序列建模实战:ARIMA建模(Stata)

Jet4505的博客 6820

Rust 异步编程 async/await

Rust 的异步编程模型 async/await 提供了一种简洁、高效的方式来处理异步操作。它允许开发者以一种更自然和直观的方式来处理异步操作,同时保持了 Rust 的安全性和性能。通过 async/await,Rust 为异步编程提供了一流的语言支持,使得编写高效且可读性强的异步程序变得更加容易。

QQ672168的博客 1344

13、数据可视化与分组操作:Danfo.js 实战指南

本文深入介绍了如何使用 Danfo.js 进行数据可视化与分组操作。内容涵盖多种图表的创建与配置,如折线图、散点图、箱线图、小提琴图、直方图和柱状图,并结合 Plotly.js 实现丰富的交互式可视化效果。同时,详细讲解了数据聚合的‘分割-应用-组合’模式,包括分组数据、遍历分组、apply 方法及 agg 聚合操作,帮助开发者高效处理和分析结构化数据。适用于 Dnotebook、HTML 及主流前端框架的应用场景。

o1p2q3r的博客 98

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

前端JavaScript篇之对async/await 的理解、async/await的优势、async/await对比Promise的优势

async/await是JavaScript中处理异步操作的一种语法糖。它让我们可以用同步的方式写异步代码,使得代码更易读懂。

星辰迷上大海的博客 2135

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

JavaScript异步编程的Async/Await

Async/Await是 JavaScript ES8引入的用于处理异步操作的语法糖,它建立在 Promises 的基础上的。使用async/await,可以以几乎同步的方式编写异步代码,极大地简化了复杂异步逻辑的处理。async关键字用于声明一个异步函数,这样的函数会隐式地返回一个 Promise。await关键字只能在async函数内部使用。

qq_53911056的博客 3712

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

async await异步

介绍Promise 既然有了promise 为什么还要有async await ? 因为promise 不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解。 Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。 Promise 对象是由关键字 new 及其 构造函数 来创建的。构造函数会 把一个叫“处理器函数”(executor fu...

陈开心的博客 2728

JavaScript 异步编程的终极指南:从回调到 Promise、Async/Await

JavaScript异步编程经历了从回调函数到Promise再到Async/Await的演变过程。回调函数导致"回调地狱"问题,代码嵌套严重,难以维护。Promise通过链式调用解决了嵌套问题,但处理复杂逻辑仍显笨拙。最终,Async/Await以同步代码风格实现了异步编程,既保持了Promise的功能,又大幅提升了代码可读性。三种方式各有特点:回调是基础,Promise是核心,Async/Await是现代首选方案。掌握这一演进路径,才能有效处理各种异步场景。

2301_79858914的博客 1080

关于c#中异步async和await的理解

之前给大家介绍了所谓异步编程的用法,但是没有细致的理解到,今天想和大家一起探讨一下;

wuyi的个人博客 2001
上一篇: 异步发展流程 —— Generators + co 让异步更优雅
下一篇: Canvas + WebSocket + Redis 实现一个视频弹幕
Panda Shen
博客等级 码龄10年 18粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值