推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
cstome

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

  •  
  •   cstome · Jan 2, 2019 · 8431 views
    This topic created in 2713 days ago, the information mentioned may be changed or developed.
    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 replies    2019-01-29 15:53:41 +08:00
    janxin
        1
    janxin  
       Jan 2, 2019
    当然不是,你还是可以用 promise 的那种写法,不需要 async 传染
    oyosc
        2
    oyosc  
       Jan 2, 2019
    async 返回的就是一个 promise 对象,你也可以直接 then 来写
    cstome
        3
    cstome  
    OP
       Jan 2, 2019
    @janxin #1
    @oyosc #2

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

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

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

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

    到底哪一层需要是异步函数,取决于程序到底可以在哪个位置并行,必然存在这么个分界点
    cstome
        12
    cstome  
    OP
       Jan 2, 2019
    @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  
       Jan 2, 2019
    看你是想写成异步还是同步了。想写成同步,也就是下一句必须等待上一句执行完毕才能执行,那就得用 await,也就必须用 async。如果你想写成异步,那就写成 then 的形式,也就不需要加 async 了。
    cstome
        14
    cstome  
    OP
       Jan 2, 2019
    @autoxbc #11 此时 c 要怎样用同步的方式调用异步?

    如果用 await 的话显然不行。
    zbinlin
        15
    zbinlin  
       Jan 2, 2019
    @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
       Jan 2, 2019
    @zbinlin #15 这样岂不是还是每一层都用 async
    zbinlin
        17
    zbinlin  
       Jan 2, 2019
    @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
       Jan 2, 2019
    @wyz123723 #13 我就是想要同步的。

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

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

    然而用了 async/await 发现这个问题。
    zbinlin
        19
    zbinlin  
       Jan 2, 2019
    @zbinlin 只要函数内有 await 才必须使用 async 定义
    autoxbc
        21
    autoxbc  
       Jan 2, 2019
    @cstome #14

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

    async function d(){}

    function c(){
    d();
    }

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

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

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

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

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

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

    对的就是有传染性,就是要 async 的
    ayase252
        33
    ayase252  
       Jan 2, 2019 via iPhone
    一个函数里有一个异步操作就是异步啊,没毛病啊。
    sagaxu
        34
    sagaxu  
       Jan 2, 2019 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  
       Jan 2, 2019 via Android
    function a() {
    (async () => {
    // 此处可以 await
    })();
    }

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

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

    deno 有一个目标也是这个

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

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

    只不过 JS 是动不动都是异步的。。。
    kcats
        46
    kcats  
       Jan 3, 2019
    @cstome 这个只是语法(内部处理机制)上的不同. 之前有和一个做 cdn 的哥们讨论过, 他习惯于写 go 和 lua, 这两个语言都有 routine 的概念, io 操作都是异步的, 但是都是同步的写法. 这和 js 就是两个极端. js 本质上暴露给用户的就是一个纯异步的环境, async/await 只是一个语法糖, 把回调变成的同步的写法. 但是本质是没有变的. 因为 js 的函数栈与事件循环机制, 决定了只有一个调用栈被清空了之后, 才能够执行下一个事务. async/await 本质上还是回调, 相当于是把后续的代码分块了, 和 python 的 event 有点类似. 说白了 async/await 就是提供了一个标记, 既是给开发人员看的, 也是给解释器看的. 如果没有这个标记还要实现相同的效果, 那整个 js 的机制和 API 规范都要改了.
    Sapp
        47
    Sapp  
       Jan 24, 2019
    同步如何能拿到异步的返回值?要么回调,要么把异步转为同步啊... 你的 b 一旦调用了 c,他就是个异步了,a 调用 b 也成了异步,你想拿到 a 返回的值,必然要么回调,要么把 a 转同步
    libook
        48
    libook  
       Jan 29, 2019
    因为 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();
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1635 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 16:15 · PVG 00:15 · LAX 09:15 · JFK 12:15
    ♥ Do have faith in what you're doing.