V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
shendaowu
V2EX  ›  JavaScript

JavaScript 中 async/await 有没有必要彻底搞懂?

  •  
  •   shendaowu · 9 天前 · 3659 次点击
    花了两天时间把相关的东西看了个大概。然后感觉跟着例子照猫画虎应该没什么大问题了,但是自己独立写很可能会有问题。感觉理解得还不是很透彻。这种不透彻是否会导致出现什么严重的问题?我是主要看的这个: https://zh.javascript.info/async 。另外还看了一些其他相关的章节。
    43 条回复    2024-04-26 23:22:24 +08:00
    AoEiuV020JP
        1
    AoEiuV020JP  
       9 天前   ❤️ 1
    我最近在研究 flutter 的 async/await 协程异步,作为原生开发者我感觉比较困扰的还是一段代码是主线程还是异步线程心里容易没数,很多操作希望是异步线程但实际只是异步协程,要真异步会很困难,
    但方便是确实方便,省了一大堆的监听,心里能把 async/await 解读成监听的话至少不影响使用,
    yor1g
        2
    yor1g  
       9 天前   ❤️ 2
    不就是 Promise 的语法糖么..
    shendaowu
        3
    shendaowu  
    OP
       9 天前
    @yor1g 可能是我没有赛博慧根吧。也可能是我对理解的要求比较高。比如对我来说能自己实现才是极致的理解。我连对 Promise 的理解都是磕磕绊绊的。我主贴链接里的题我都没全答对。还有可能是没时间用各种方式“玩”需要学的技术。

    顺便抱怨一下,感觉大量简单的题会更有帮助一些。感觉那个网站的题太难了。
    kneo
        4
    kneo  
       9 天前 via Android   ❤️ 2
    不是嘲讽你,我有点没理解,你自己都明确觉掌握不足了。一般这就像渴了喝水一样继续找点资料看下去。你还在纠结要不要深入学习的原因是什么?不喜欢编程?没有时间?
    ysc3839
        5
    ysc3839  
       9 天前 via Android   ❤️ 1
    我觉得没必要了解微任务,至少我自己是没了解过,目前也没影响我用 js 写代码。async function 方面,知道实际实现是类似在 await 处拆成多个代码块就差不多了。
    例如以下代码:
    async function fn() {
    await a;
    const result = await b;
    console.log(result);
    }
    可以实现成:
    function fn() {
    a.then(() => {
    b.then(result => {
    console.log(result);
    });
    });
    }
    能看作类似回调函数的语法糖。
    不过 C++ 的 coroutine 是拆成 switch case 块,而不是拆成多个回调函数,这个了解就行。
    okakuyang
        6
    okakuyang  
       9 天前   ❤️ 2
    我都不知道这有啥好搞懂的,不就是用同步的语法写异步的函数,除此之外还有任何需要在意的吗?
    favourstreet
        7
    favourstreet  
       9 天前 via Android   ❤️ 3
    这么多层了还没有人推荐楼主去把 Monad 学明白了回来降维打击 async/await 吗
    shendaowu
        8
    shendaowu  
    OP
       9 天前
    @kneo 就是没时间,你猜对了。如果我财富自由了学所有主流编程语言肯定会排在前十。现在能凑合写出没什么大问题的代码就行了。
    shendaowu
        9
    shendaowu  
    OP
       9 天前
    @favourstreet 我没学过函数式编程,学明白 Monad 要很长时间吧?
    chenliangngng
        10
    chenliangngng  
       9 天前   ❤️ 1
    @favourstreet 一个精巧的玩具,最直接的产物就是 Promise ,然而即使 Promise 也不是个方便理解的东西
    fanyange
        11
    fanyange  
       9 天前   ❤️ 1
    一句话解释就是,async/await 是 promise 的语法糖,本质是一个普通的函数,但这个函数的返回值作为一个 promise 里 resolve 的值。其次,这个函数里调用的所有其他异步函数都可以通过 await 来得到其 resolve 之后的值,像同步函数的写法。

    当然你也可以不在 async 函数体内部写 await ,那里面的异步函数就不会阻塞,各执行各的。
    Rocketer
        12
    Rocketer  
       9 天前 via iPhone   ❤️ 1
    不懂也不耽误用,想理解底层原理就去看一下 JS 事件循环
    AoEiuV020JP
        13
    AoEiuV020JP  
       9 天前 via Android   ❤️ 1
    @AoEiuV020JP #1 比起 async 感觉 flutter 的 async*更难搞,折腾几天的协程主要问题都在 async*和 Stream 这边,数据层层传递时还要顾及 done 和 error ,有时候必须 listen 有时候又必须 async* await for 的,
    普通的 async/await 感觉主要调试时混乱一些,
    shizhibuyu2023
        14
    shizhibuyu2023  
       9 天前   ❤️ 2
    楼上的各位,阮一峰的 ES6 都不看的吗?
    JS 的 async/await 明明是用 co 模块自动调用 Promise 的迭代器( generator 语法生成)的语法糖
    网上各种带你手写的视频、文章,自己花一天时间看一遍不难吧
    这些东西跟 Vue 的模板语法、React 的 JSX 语法一样,你不理解背后的原理也能写,但去了解背后的原理并不会花太多时间,又不是让你去手写编译器
    rabbbit
        15
    rabbbit  
       9 天前   ❤️ 1
    追求极致的理解?看这个🐶
    https://tc39.es/ecma262/#sec-async-function-definitions
    rabbbit
        16
    rabbbit  
       9 天前   ❤️ 1
    rabbbit
        17
    rabbbit  
       9 天前   ❤️ 1
    开个玩笑,个人觉得知道用法、常见的坑就可以了。
    再往上如果能懂得原理,自己实现更好,当然没必要强求,找工作另算还是直接背八股更快。

    这是我以前“研究”奇怪语法背后的原因时写的,现在一看,纯吃饱了撑的。
    https://aaron-bird.github.io/article/FunctionDeclarationInstantiation/
    orioleq
        18
    orioleq  
       9 天前 via iPhone   ❤️ 1
    @kneo 身为前 1%的程序员,对中位数的程序员说找水找错地方了,听上去还真的不像“嘲讽”…就好像学霸觉得自己的学习经验没啥好分享的都是直觉潜意识,还总劝普娃别走学习这条路了。有些人一边劝经验丰富的程序员安心写代码别瞎寻思转行,一边劝经验没那么丰富的程序员早点转行,多少有点二极管思维吧?
    NessajCN
        19
    NessajCN  
       9 天前 via Android   ❤️ 1
    这东西多写写就懂了。
    编程的好多概念都是看的时候一头雾水,
    写到需要用到这些个工具的时候立刻恍然大悟了。
    DOLLOR
        20
    DOLLOR  
       9 天前   ❤️ 1
    我觉得只有经历过 callback hell 时代,才能理解为什么会有 promise ,为什么会有 async/await 。
    所以建议楼主先写一段时间 callback ,自然会慢慢理解了。
    rioshikelong121
        21
    rioshikelong121  
       9 天前   ❤️ 1
    我曾经自认为搞的懂那么一点点过,,研究过 Task Queue, Micro Task Queue, 读过相关的 Spec, 看过各种相关的八股题目。读过 Promise/A+ 规范也照猫画虎实现过。
    然而现在写了两年多的后端代码以后忘的差不多了。
    非要我自己说,本质上 Promise 也好,Async/Await 也好,都只是下层提供的抽象,绝大部分情况下直接拿过来用可以满足大部分的场景的需要的。
    花费大量精力搞懂这些的原因可能是:

    1. 满足自己的好奇心
    2. 通过理解一些偏底层的原理,在 1% 的场景下可以帮助你更快的定位 BUG / 问题从而解决问题,也就是为了解决抽象泄露才不得不去学习底层。
    3. 可能也是最重要的:用来在面试的时候筛选人或者被我筛选。


    现在让我来看的话,我自己可能对学这些知识兴趣不是那么大了,毕竟 AGI 都快实现了,学这些对于大部分的普通开发者来说性价比很低。
    iqoo
        22
    iqoo  
       9 天前   ❤️ 1
    完全有必要,代码可读性大幅提升
    leokun
        23
    leokun  
       9 天前   ❤️ 1
    @favourstreet Promise 不是一个严格的 Monad ,不过挺像的
    angrylid
        24
    angrylid  
       9 天前 via Android   ❤️ 1
    不应付面试的话,JavaScript.info 真的足够应付业务开发的大多数问题了,遇到坑你再深入原理不迟。

    赞成 17 楼所说。
    RedNax
        25
    RedNax  
       9 天前   ❤️ 2
    要深入研究也很简单,去 https://www.typescriptlang.org/play 写一点有 async/await 的 TS 代码,右边就有实时编译的 js 代码。
    TS Config 的 Target 调整到 ES2016 (还没有 async/await ),就能看到 async/await 是怎么用 generator 实现的。
    Target 调整到 ES5 (还没有 Generator ),就能看到 Generator 又是怎么用状态机实现的。
    hukei
        26
    hukei  
       8 天前   ❤️ 1
    最近刚看 讲的最清楚 最简洁的教程视频 B 站虎哥 https://www.bilibili.com/video/BV1ig4y137Wr/
    JavenXiao
        27
    JavenXiao  
       8 天前   ❤️ 1
    做一些宏任务、微任务谁先打印出来的题,搞清楚 js 的事件循环
    然后自己手搓一遍 Promise ,实现 Promise 的各种 api
    然后努力理解一下 Generator 函数

    最后组合起来,你就能理解 async/await 到底是个啥了
    wjx0912
        28
    wjx0912  
       8 天前   ❤️ 1
    Moierby
        29
    Moierby  
       8 天前   ❤️ 1
    精通了“茴”字的 4 种写法,也不影响大龄被裁员
    waiaan
        30
    waiaan  
       8 天前   ❤️ 1
    @shizhibuyu2023
    这才是 async/await 的正解。
    hazardous
        31
    hazardous  
       8 天前   ❤️ 1
    会用就行,完全不需要了解底层。用熟了之后有兴趣就再细研究。
    wolfan
        32
    wolfan  
       8 天前   ❤️ 1
    正常开发不用,除非你是资深技术岗,需要对贵司的业务线优化和提高性能。
    mioktiar56
        33
    mioktiar56  
       8 天前   ❤️ 1
    当你问出这个问题的时候,答应其实已经出来了
    hi2hi
        34
    hi2hi  
       8 天前   ❤️ 2
    一个没经历过`*function`的好运人
    libook
        35
    libook  
       8 天前 via Android   ❤️ 1
    10 年 JS 全栈经验,个人认为 async/await 是非常常用的东西,有必要掌握机制和原理。

    属于 JS 异步机制,你可以通过了解 JS 事件循环、Promise 的原理来理解 async/await 。
    wanguorui123
        36
    wanguorui123  
       8 天前   ❤️ 1
    很简单 Promise = async function
    forty
        37
    forty  
       8 天前   ❤️ 1
    不太清楚有什么理解困难的。

    无他,2 点:
    1. 调用 async 异步函数的时候,如果你本意是有顺序要求的,一定要记得加 await ,否则顺序就不保证了,会出现非预期的效果。
    2. 做好错误捕捉 try...catch.
    morenacl
        38
    morenacl  
       8 天前   ❤️ 1
    但凡问“有没有必要”,就是没必要
    agagega
        39
    agagega  
       8 天前   ❤️ 2
    计算机的知识从来都是可深可浅。其实所有领域的知识都是这样,但因为计算机整个体系人造属性特别强,所以这个特征表现得更明显。

    对于 async/await ,最简单的理解:某些函数需要加 await 才能调用,用到 await 的函数需要用 async 标记,而调 async 函数也需要 await 。

    更深一点:await 的其实是一个 Promise 对象,所有 async/await 的代码都可以改写为 Promise 和 then 。还有个叫 Generator 的东西,它的思路和 Promise 完全不一样。更古早的代码里还有种写法叫回调函数。这四者可以相互改写。

    为什么要这样?因为一些操作(如网络请求、读写文件)需要等待取回数据,在等待过程中可以让 CPU 做其他的事情提高效率,等到数据读完再回来执行下一步代码,所以有了回调函数,这种写法叫做异步。NodeJS 里有同步读写文件的方法,可以和异步写法对比感受差异。

    因为浏览器环境强调实时响应,所以 JS 一开始就很重视异步,这也是其他语言容易实现 sleep 函数而 JS 很麻烦的原因。

    为什么有这么多种写法?程序运行的一个本质问题是,下一步要执行什么。通常的逻辑是,如果是普通语句就正常执行,如果是函数调用就传参进入,如果是 return 则回到上一层调用继续执行。

    假如所有代码都写到一个 C 函数里,那我们不需要考虑这么多复杂的问题,因为我们可以自由地自己做调度。但我们希望在保证代码结构性(拆成函数)的同时,能够继续保证效率(在等待网络响应时能执行别的代码)。对于有匿名函数的语言,最直接的想法就是回调函数,而且通常这个回调函数除了固定的参数外,还要能从发请求的地方抓一些变量写东西(这被称作闭包)。回调函数的思路和处理网页事件是一致的。

    但回调函数的写法意味着,每次要发送请求或者干什么事情时,都要在上一层回调函数里再新建一层回调函数,缩进层次会特别深,这被称作回调地狱( callback hell )。更麻烦的是,真实代码的逻辑可能不是线性的,可能有一个 if 或循环,里面执行一些异步代码,然后再出来。这种时候写回调函数简直就是脑力大作战。

    换种思路,我们能不能发明一种特殊的函数,比传统函数更强大?传统函数一旦 return ,只能再从头开始执行。我们想要的这个新函数,可以从中间返回,下次再从上次返回的地方继续执行。这样我们就不用写回调了,发请求的时候返回,等响应到了再跳回来就可以了。JS 本身就有闭包,且支持函数套函数(参考计数器的写法),这种功能理论上只需要实现一个返回函数的函数就行了,但 JS 还是有一个专门的语法,叫生成器( generator )。

    而回调函数的写法也有改进空间。如果能把异步操作封装进一个对象里,然后动态添加每一层的回调,虽然还得写很多匿名函数,但缩进上至少可以拍平了。这就是 Promise ,它用 then 函数连接下一个回调。

    如果再把一层层的 then 里的函数也抽出来,那 then 就变成了 await 。而对于用到了 await 的函数,它的执行流程和生成器就非常像了,在 await 的地方跳出去交还给 runtime ,等到合适的时候再跳回来。所以用到 await 的函数和生成器生成的函数一样,不是普通函数,必须要有区别,所以有 async 标记。但到这里应该能理解,这几种写法是等价的,await 一个 async 函数,和 await 一个返回 Promise 的普通函数,是一回事。

    关于回调函数的讨论还可以更进一步。回调函数的写法,是把事件响应后要执行的代码封装成一个函数。但如果我把所有要执行的语句都写成这个样子呢?比如正常的 a=b+1;b=c+1 ,把后一句也写在一个回调函数里面会怎么样?除了极度蛋疼外,你也会获得极度的灵活性。

    实际上,这个由接下来的代码组成的回调函数有个学名,叫 continuation (有时中文翻译作续体)。一些语言(如 Lisp 、Ruby )直接提供了操作 continuation 对象的能力,但这个功能太强大,所以衍生出一种弱一些的变体,叫 delimited continuation ,也就是需要指定这个接下来执行的函数到哪为止。Kotlin 里面带 @ 符号的 return 就有点这个味。

    而 async/await 的语法也可以进一步泛化。await 的逻辑是跳出去再跳回来,形式上是否和异常的 try catch 有点像?特别某些语言的异常还真的支持 retry 语句。还真的有种语法概念把 await 和异常统一起来,叫 algebraic affects 。所以 await 真的没什么神秘的。

    到这一步其实还有很多问题没有深入,比如这些 runtime 在操作系统和原生代码层面是怎么实现和封装的,其他模型比如 go 的 goroutine 和传统的多进程模型等。但我想你现在理解 async/await 一定容易了很多:)
    Leviathann
        40
    Leviathann  
       8 天前   ❤️ 1
    从两个方面了解把,一个顶层表达,一个底层实现

    编程语言,首先是一门语言,有它想要表达的语义
    但是抽象泄露是无法避免的,所以需要去了解底层实现,让你在用它表达的时候更有把握
    zhouS9
        41
    zhouS9  
       8 天前   ❤️ 1
    await/async 解决了 promise 的问题,但是我喜欢用 promise ,因为他解决了 callback 的问题
    ynyjyz
        42
    ynyjyz  
       8 天前   ❤️ 1
    Promise 、async 和 await 是 JavaScript 语言中处理异步操作的三个重要概念。下面我将分别解释它们的含义和用法:

    Promise
    Promise 是一种用于异步计算的对象。它代表了一个可能现在还没有结果,但将来会有结果的值。一个 Promise 对象有三种状态:

    Pending (进行中):初始状态,既不是成功,也不是失败状态。
    Fulfilled (已成功):操作成功完成。
    Rejected (已失败):操作失败。
    const myPromise = new Promise((resolve, reject) => {
    // 异步操作
    const condition = true; // 假设这是异步操作的结果
    if (condition) {
    resolve('Promise is resolved successfully.');
    } else {
    reject('Promise is rejected.');
    }
    });

    myPromise.then((value) => {
    console.log(value); // 如果成功,将打印 'Promise is resolved successfully.'
    }).catch((error) => {
    console.error(error); // 如果失败,将打印 'Promise is rejected.'
    });
    async/await
    async 和 await 是基于 Promise 的语法糖,它们使得异步代码的编写和同步代码一样直观。

    async:用于声明一个函数是异步的。这意味着函数内部可能会执行一个或多个异步操作,并且函数返回的是一个 Promise 对象。
    await:用于等待一个 Promise 对象的结果。它只能在 async 函数内部使用。
    async function asyncFunction() {
    try {
    const result = await myPromise; // 等待 myPromise 的结果
    console.log(result); // 'Promise is resolved successfully.'
    } catch (error) {
    console.error(error); // 'Promise is rejected.'
    }
    }

    asyncFunction();
    使用 async 和 await 可以避免所谓的“回调地狱”,并使异步代码的逻辑更加清晰。

    区别和联系
    Promise 是一种异步操作的模式,async/await 是基于 Promise 的更简洁的语法。
    async 函数返回一个 Promise ,await 使得你可以暂停 async 函数的执行,等待 Promise 的解决。
    Promise 需要使用 .then() 和 .catch() 来处理结果和错误,而 async/await 使用 try/catch 来处理。
    这些概念是现代 JavaScript 异步编程的基础,它们允许开发者以一种更接近同步编程的方式来处理异步任务。
    fpure
        43
    fpure  
       8 天前
    @yor1g 不一样的,光有 Promise 不能实现 async/await 语法,还需要 generator 。应该说在这个结构里,generator 比 Promise 重要,比如 rust 里面用的就是基于轮询的 Future 而非基于回调的 Promise
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   742 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 21:58 · PVG 05:58 · LAX 14:58 · JFK 17:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.