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

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

  •  
  •   cstome · 108 天前 · 2959 次点击
    这是一个创建于 108 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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
        1
    janxin   108 天前
    当然不是,你还是可以用 promise 的那种写法,不需要 async 传染
        2
    oyosc   108 天前
    async 返回的就是一个 promise 对象,你也可以直接 then 来写
        3
    cstome   108 天前
    @janxin #1
    @oyosc #2

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

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

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

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

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

    如果用 await 的话显然不行。
        15
    zbinlin   108 天前
    @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(..., ...);
        16
    cstome   108 天前
    @zbinlin #15 这样岂不是还是每一层都用 async
        17
    zbinlin   108 天前
    @cstome

    function A() {
    return B();
    }

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

    //Some logic code
    return someResult;
    }

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

    A().then(..., ...)
        18
    cstome   108 天前
    @wyz123723 #13 我就是想要同步的。

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

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

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

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

    async function d(){}

    function c(){
    d();
    }

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

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

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

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

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

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

    对的就是有传染性,就是要 async 的
        33
    ayase252   108 天前 via iPhone
    一个函数里有一个异步操作就是异步啊,没毛病啊。
        34
    sagaxu   108 天前 via Android
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function

    async function expression used as an IIFE,你可以用 IIFE 斩断这种传染性。
        35
    sagaxu   108 天前 via Android
    function a() {
    (async () => {
    // 此处可以 await
    })();
    }

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

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

    deno 有一个目标也是这个

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

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

    只不过 JS 是动不动都是异步的。。。
        46
    kcats   107 天前
    @cstome 这个只是语法(内部处理机制)上的不同. 之前有和一个做 cdn 的哥们讨论过, 他习惯于写 go 和 lua, 这两个语言都有 routine 的概念, io 操作都是异步的, 但是都是同步的写法. 这和 js 就是两个极端. js 本质上暴露给用户的就是一个纯异步的环境, async/await 只是一个语法糖, 把回调变成的同步的写法. 但是本质是没有变的. 因为 js 的函数栈与事件循环机制, 决定了只有一个调用栈被清空了之后, 才能够执行下一个事务. async/await 本质上还是回调, 相当于是把后续的代码分块了, 和 python 的 event 有点类似. 说白了 async/await 就是提供了一个标记, 既是给开发人员看的, 也是给解释器看的. 如果没有这个标记还要实现相同的效果, 那整个 js 的机制和 API 规范都要改了.
        47
    Sapp   86 天前
    同步如何能拿到异步的返回值?要么回调,要么把异步转为同步啊... 你的 b 一旦调用了 c,他就是个异步了,a 调用 b 也成了异步,你想拿到 a 返回的值,必然要么回调,要么把 a 转同步
        48
    libook   81 天前
    因为 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();
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1606 人在线   最高记录 4385   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 19ms · UTC 01:04 · PVG 09:04 · LAX 18:04 · JFK 21:04
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1