ES 中要用 await,上一层的函数都要是 async 的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
cstome
V2EX    Javascript

ES 中要用 await,上一层的函数都要是 async 的?

  •  
  •   cstome 2019-01-02 14:25:02 +08:00 8018 次点击
    这是一个创建于 2475 天前的主题,其中的信息可能已经有所发展或是发生改变。
    async function A() { let someData = await B(); return someData; } async function B() { let someData = await C(); //Some logic code return someResult; } async function C() { return new Promise(); } A(); 

    上面实例的,只有 C 是异步的,B 在调用 C 同步执行的时候,B 必须是异步函数。而 A()在调用 B 时需要 B 通过 C 的返回经过 B 的某些计算,再返回给 A,因此调用 B 的时候也要是同步的。想要让 B 同步,A 就必须是异步函数。

    这样的话岂不是想要用 await,上层所有函数都必须是异步函数?

    48 条回复    2019-01-29 15:53:41 +08:00
    janxin
        1
    janxin  
       2019-01-02 14:32:12 +08:00
    当然不是,你还是可以用 promise 的那种写法,不需要 async 传染
    oyosc
        2
    oyosc  
       2019-01-02 14:38:20 +08:00
    async 返回的就是一个 promise 对象,你也可以直接 then 来写
    cstome
        3
    cstome  
    OP
       2019-01-02 14:42:38 +08:00
    @janxin #1
    @oyosc #2

    我知道,但是这样似乎还是没法优雅的解决回调的问题。
    janxin
        4
    janxin  
       2019-01-02 14:46:43 +08:00
    @cstome promise 不就是为了解决回调提出来的方法么?
    oyosc
        5
    oyosc  
       2019-01-02 14:47:22 +08:00
    @cstome 相对于回调地狱这种来说,已经很优雅了,更直观的表示,如果你是指完全的同步这种问题,那应该是没有...
    lrz0lrz
        6
    lrz0lrz  
       2019-01-02 14:49:53 +08:00
    是这样的,async 是用来指定 await 的范围,如果没有 async,js 就不知道哪些代码需要等待 await 执行完成。

    另外楼主这个例子,a 依赖 b 的结果,b 的结果依赖 c 的结果,c 的结果依赖异步的结果,所以 a 依赖异步的结果,理应是异步的呀?
    shintendo
        7
    shintendo  
       2019-01-02 14:52:05 +08:00
    你想要哪一层用 await,就在那一层用 async,与上一层无关,上一层仍然可以 promise
    如果你想要每一层都 await,自然每一层都要 async
    async/await 不过是语法糖,再牛逼也不能真的把异步变同步啊
    sagaxu
        8
    sagaxu  
       2019-01-02 14:52:42 +08:00 via Android
    await 只能出现在 async 函数,但是普通函数可以调用 async 函数
    geelaw
        9
    geelaw  
       2019-01-02 14:53:22 +08:00 via iPhone   2
    async 的作用是启用该 context 内的自动 CPS 变换(也就是同步风格代码翻译成异步),await 的作用是表明这里是一个 CPS 变换的 checkpoint。

    用同步的风格写异步的代码 = 用 await,从而包裹之的 function 必须用 async 修饰(也就是“启用 await ”)。

    ES 的 async/await 和 C# 的一样。
    CloudnuY
        10
    CloudnuY  
       2019-01-02 14:54:15 +08:00
    你让一个人帮你去楼下买东西,你必须在楼上等着他买回来,总不能自己出去逛街吧……
    autoxbc
        11
    autoxbc  
       2019-01-02 14:57:11 +08:00
    async 传染的本质:对于末端是异步的函数,在整个调用链上,从分界点(同步函数以同步形式调用异步函数)开始,到末端,要全部显式声明 async
    a -> b -> c(以同步形式调用 d) -> d(异步) -> e(异步) -> f(异步)
    d,e,f 必须是 async ; a,b,c 完全不需要

    到底哪一层需要是异步函数,取决于程序到底可以在哪个位置并行,必然存在这么个分界点
    cstome
        12
    cstome  
    OP
       2019-01-02 15:00:10 +08:00
    @janxin #4
    @oyosc #5

    仔细想了一下,你们所说的改成 Promise 是怎么改?

    能拿我的例子改一下吗?

    改成这样?

    ```
    function A() {
    let someData = B().then(res => {
    return res;
    })
    }

    function B() {
    let someData = C().then(res => {
    //Some logic code
    return someResult;
    })
    }

    async function C() {
    return new Promise();
    }

    A();
    ```
    wyz123723
        13
    wyz123723  
       2019-01-02 15:01:36 +08:00
    看你是想写成异步还是同步了。想写成同步,也就是下一句必须等待上一句执行完毕才能执行,那就得用 await,也就必须用 async。如果你想写成异步,那就写成 then 的形式,也就不需要加 async 了。
    cstome
        14
    cstome  
    OP
       2019-01-02 15:03:46 +08:00
    @autoxbc #11 此时 c 要怎样用同步的方式调用异步?

    如果用 await 的话显然不行。
    zbinlin
        15
    zbinlin  
       2019-01-02 15:06:15 +08:00
    @cstome

    async function A() {
    return B();
    }

    async function B() {
    let someData = await C();

    //Some logic code
    return someResult;
    }

    async function C() {
    return new Promise();
    }

    A().then(..., ...);
    cstome
        16
    cstome  
    OP
       2019-01-02 15:08:18 +08:00
    @zbinlin #15 这样岂不是还是每一层都用 async
    zbinlin
        17
    zbinlin  
       2019-01-02 15:11:38 +08:00
    @cstome

    function A() {
    return B();
    }

    async function B() {
    let someData = await C();

    //Some logic code
    return someResult;
    }

    function C() {
    return new Promise();
    }

    A().then(..., ...)
    cstome
        18
    cstome  
    OP
       2019-01-02 15:12:05 +08:00
    @wyz123723 #13 我就是想要同步的。

    比方说我用 axios 请求数据,必须根据请求结果才能进行判断,执行下一步。

    如果用 Promise 方法就只能一直 then 下去,感觉整个程序都是写在 then 里,不优雅。

    然而用了 async/await 发现这个问题。
    zbinlin
        19
    zbinlin  
       2019-01-02 15:13:02 +08:00
    @zbinlin 只要函数内有 await 才必须使用 async 定义
    autoxbc
        21
    autoxbc  
       2019-01-02 15:20:28 +08:00
    @cstome #14

    如果 c 不需要 d 的返回值(既不需要异步状态的真实返回值,也不需要同步状态的 promise ),c 就是分界点

    async function d(){}

    function c(){
    d();
    }

    你的例子无法改写,分界点在更高的位置。上面说用 promise 改写的理解有误,用了 async 和 await 就不应在调用链里写任何 promise
    cstome
        22
    cstome  
    OP
       2019-01-02 15:21:27 +08:00
    @zbinlin #19 加入我在 A 里面也需要获取 B 的结果在进行处理呢?

    还是不可避免的要把 A 变成异步函数。

    又或者使用 Promise 的话,就只能把后面的逻辑都写在 then 里:

    ```js
    function A() {
    B().then(res => {
    //some logic
    return someResult;
    })
    }
    ```

    这样看起来就是不太好。。。
    janxin
        23
    janxin  
       2019-01-02 15:34:48 +08:00
    @cstome 这就是显示切换的缺点,想兼容不想大改只有用这种方式
    zbinlin
        24
    zbinlin  
       2019-01-02 15:48:08 +08:00
    @cstome 使用 async 函数有什么影响吗(不好的地方)?
    jin5354
        25
    jin5354  
       2019-01-02 15:56:05 +08:00
    @cstome C 是个异步函数,B 调用了 C 且依赖 C 的返回值,那 B 肯定也是异步函数啊,同理 A,async/await 关键字就是在表明本函数是异步函数,但是可用类同步的姿势写,不可能跟同步写的一模一样的,设计出来就不是完全无感知的,你原文写法没毛病
    shintendo
        26
    shintendo  
       2019-01-02 16:00:36 +08:00
    A 是否要等待 B 的结果,和 B 是否要等待 C 的结果,是两个不相关的异步事件,你想把哪个写成同步,就对哪个用 async/await,不能指望写了其中一个,另一个也自动变成同步了呀。
    otakustay
        27
    otakustay  
       2019-01-02 16:37:13 +08:00
    async 就是个标记,当你需要等一个异步函数的时候,无论它的调用方是不是 async,都注定是异步了(用 promise 的 then 也一样是异步)
    abc635073826
        28
    abc635073826  
       2019-01-02 16:44:36 +08:00
    @CloudnuY 只有你说到了重点
    cstome
        29
    cstome  
    OP
       2019-01-02 18:03:43 +08:00
    @abc635073826 #28
    @CloudnuY #10

    JS 的逻辑还就真是买东西的 Promise 把东西买回来就行,你爱上哪逛上哪逛。
    cstome
        30
    cstome  
    OP
       2019-01-02 18:05:10 +08:00
    @zbinlin #24 就是这样的话几乎所有函数都是 async,或者 Promise,感觉都不太好。
    lzvezr
        31
    lzvezr  
       2019-01-02 18:29:44 +08:00 via Android
    你可以直接 return 一个 promise,在 then 里面 return 最终会被捕获
    ```评论不支持 markdown
    function A() {
    return B().then(res => {
    //some logic
    return someResult;
    })
    }
    await A()
    最终得到的是 someResult
    shynome
        32
    shynome  
       2019-01-02 18:30:28 +08:00 via Android
    别用 async 和 Promise 了, cb 一直写下去吧,性能又好

    对的就是有传染性,就是要 async 的
    ayase252
        33
    ayase252  
       2019-01-02 18:39:45 +08:00 via iPhone
    一个函数里有一个异步操作就是异步啊,没毛病啊。
    sagaxu
        34
    sagaxu  
       2019-01-02 19:33:12 +08:00 via Android
    https://developer.mozilla.org/en-US/docs/Web/Javascript/Reference/Operators/async_function

    async function expression used as an IIFE,你可以用 IIFE 斩断这种传染性。
    sagaxu
        35
    sagaxu  
       2019-01-02 19:38:40 +08:00 via Android
    function a() {
    (async () => {
    // 此处可以 await
    })();
    }

    a 是一个没有 async 的函数,a 里面使用了 await
    zbinlin
        36
    zbinlin  
       2019-01-02 19:47:33 +08:00
    @cstome 没什么不好的,看多了就习惯了,真的。
    kcats
        37
    kcats  
       2019-01-02 19:51:04 +08:00
    tc39 有个新的提议: top level await, 参见 https://github.com/tc39/proposal-top-level-await , 目前是 stage 2, 最新版 v8 已经实现了这个 proposal, 所以你可以在 chrome 控制台直接写 await 1 回车测试.
    kcats
        38
    kcats  
       2019-01-02 19:52:23 +08:00
    @kcats 不过好像和楼主的问题无关...
    des
        39
    des  
       2019-01-02 22:53:57 +08:00 via Android
    虽然不是上层必须 async,但还是 async 舒服。
    不传染的话,你就得自己等待返回结果,然后处理

    顺便提一下一个有意思的东西,fibjs,就是为了解决这些的
    TwoDays91
        40
    TwoDays91  
       2019-01-03 08:11:00 +08:00 via iPhone
    如果你逻辑依赖异步那你只能这么写,你还没试过每个 async 都要写 try catch。建议风格统一不要 promise 混着用。
    jjx
        41
    jjx  
       2019-01-03 08:16:06 +08:00
    @kcats

    deno 有一个目标也是这个

    await 传染很容易出 bug, 不过大都可以在测试层面解决掉
    AV1
        42
    AV1  
       2019-01-03 08:33:56 +08:00 via Android
    callback, Promise, async function 都有传染性,只是语法糖甜度不同,该异步的仍要异步
    abc635073826
        43
    abc635073826  
       2019-01-03 15:12:02 +08:00
    @cstome 本质上东西回来了你是要拿到的,它总有一个归属地
    kcats
        44
    kcats  
       2019-01-03 15:16:08 +08:00
    @jjx 这是正常的吧, 一个异步的函数在外部还能搞成同步的? 那异步的作用有啥意义? 楼主的问题是自己没想清楚, top level await 解决的问题是在没有 async 函数标记的情况下同步写异步代码, 和楼主的问题是两码事.
    cstome
        45
    cstome  
    OP
       2019-01-03 15:36:39 +08:00
    @kcats #44 其实我就是想把异步的强制变同步。

    说实话,在大部分编程中,同步的情况比异步要多。

    只不过 JS 是动不动都是异步的。。。
    kcats
        46
    kcats  
       2019-01-03 16:24:03 +08:00
    @cstome 这个只是语法(内部处理机制)上的不同. 之前有和一个做 cdn 的哥们讨论过, 他习惯于写 go 和 lua, 这两个语言都有 routine 的概念, io 操作都是异步的, 但是都是同步的写法. 这和 js 就是两个极端. js 本质上暴露给用户的就是一个纯异步的环境, async/await 只是一个语法糖, 把回调变成的同步的写法. 但是本质是没有变的. 因为 js 的函数栈与事件循环机制, 决定了只有一个调用栈被清空了之后, 才能够执行下一个事务. async/await 本质上还是回调, 相当于是把后续的代码分块了, 和 python 的 event 有点类似. 说白了 async/await 就是提供了一个标记, 既是给开发人员看的, 也是给解释器看的. 如果没有这个标记还要实现相同的效果, 那整个 js 的机制和 API 规范都要改了.
    Sapp
        47
    Sapp  
       2019-01-24 10:17:52 +08:00
    同步如何能拿到异步的返回值?要么回调,要么把异步转为同步啊... 你的 b 一旦调用了 c,他就是个异步了,a 调用 b 也成了异步,你想拿到 a 返回的值,必然要么回调,要么把 a 转同步
    libook
        48
    libook  
       2019-01-29 15:53:41 +08:00
    因为 A 必须依赖 B 执行完才可以继续执行,同时 B 也依赖 C 执行完才能继续执行,所以不管你用 callback 还是 Promise 还是 async,都逃不掉三者都做同步化处理:

    callback 版本:

    function A(resultFromB) {
    let someData = resultFromB;
    return someResult;
    }

    function B(resultFromC, cb) {
    let someData = resultFromC;

    //Some logic code
    cb(someResult);
    }

    function C(cb) {
    (new Promise()).then((result) => {
    cb(result, A);
    });
    }

    C(B);

    Promise 版本:

    function A(resultFromA) {
    let someData = resultFromA;

    return someData;
    }

    function B(resutlFromC) {
    let someData = resutlFromC;

    //Some logic code
    return someResult;
    }

    function C() {
    return new Promise();
    }

    C.then(B).then(A).then((resultFromA) => {
    //Do something.
    });

    避免不了的,但是外层层都用 async 不是因为内层用了 async,而是因为外层关心内层执行完的结果,如果不关心的话完全可以不用 async。

    function B() {
    new Promise();
    }

    function A() {
    B();//我不关心 B 执行完返回啥,就让他自生自灭吧
    //继续执行其他的代码
    }

    A();
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5478 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 01:26 PVG 09:26 LAX 18:26 JFK 21:26
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86