V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ecnelises
V2EX  ›  编程

有语言的 async/await 是直接改写成嵌套回调的吗?

  •  
  •   ecnelises · 2022-01-26 00:26:14 +08:00 · 1828 次点击
    这是一个创建于 813 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看到这个隔壁帖子( https://www.v2ex.com/t/830515 )有感。async/await 的基本逻辑是这样:

    async myFunction() {
      // some code
      await readSomething() // 执行至此处函数交出控制权,读取完成后回到此处继续执行
      // some code
      await sendRequest() // 再次交出控制权
    }
    

    和这个模型最为贴近的是协程(生成器),await 相当于一次 yield ,然后等待外面 call 自己的作为调度器的某个东西再 resume 进来。Swift 5.5 加入了 async/await ,把这个调度器做到了系统的事件循环里,所以(起初)需要新系统支持。

    然后查了一下 TypeScript 和 Rust 的实现,都是基于状态机,原理其实和 Swift 的类似。但换种方向直观理解,async/await 好像也可以改写成嵌套回调,比如:

    // some code
    readSomething(() => {
      // some code
      sendRequest(() => {
        // some code
      })
    })
    

    因为有闭包,所以循环也可以通过改成尾递归再写成这种格式。了解了下这种格式有个学名叫 Continuous-passing style ( https://en.wikipedia.org/wiki/Continuation-passing_style ),看起来也很合理。但为什么很多编程语言都选择了另一种实现方式呢?

    10 条回复    2022-01-26 09:54:25 +08:00
    lhx2008
        1
    lhx2008  
       2022-01-26 00:30:44 +08:00
    还是 go 的写法比较符合人类直觉
    shenzye
        2
    shenzye  
       2022-01-26 00:53:01 +08:00 via Android   ❤️ 1
    按我的理解,async/await 直接改写成嵌套回调是一种有成本的抽象。嵌套回调通过闭包传递变量,那么整个方法执行下来,需要占用所有被使用过的变量的空间(比如使用过然后又释放掉的变量也会占用空间),而基于状态机的异步实现,可以重复利用已经不再使用的变量的空间。
    如若有误,感谢各位指正
    xarthur
        3
    xarthur  
       2022-01-26 00:56:57 +08:00 via iPhone
    因为回调的写法会有回调地狱( callback hell )。
    而且你可以试试在回调的写法里来处理错误,会更加头痛。
    其实最好的处理方法还是 do notation
    xarthur
        4
    xarthur  
       2022-01-26 01:02:03 +08:00 via iPhone
    另外 CPS 也是可以转换成同步的写法的,你可以搜索「 CPS 变换」。
    molvqingtai
        5
    molvqingtai  
       2022-01-26 01:57:57 +08:00 via Android
    问反,就是因为回调地狱,才使用 async/await
    eason1874
        6
    eason1874  
       2022-01-26 02:07:20 +08:00
    JS 近几年才有 async/await ,以前写异步只能回调,别提多难受了,一个事务里有多个异步调用的时候只能把上下文传入回调函数,传来传去
    Rocketer
        7
    Rocketer  
       2022-01-26 02:17:09 +08:00 via iPhone
    我学 ES6 的时候,教程里说 await 就是语法糖,它最终会被编译成回调。
    autoxbc
        8
    autoxbc  
       2022-01-26 02:39:38 +08:00
    JS 不是一下子只引入 async/await 的,而是和 Promise ,Generator 一起引入的,所以把 async/await 渐进的 polyfill 成 Generator ,Promise 是比较自然的。就像要去 4 楼,可以从 1 楼坐电梯,不过如果刚好也要去 2 楼 3 楼,那不如一层一层爬楼梯
    EPr2hh6LADQWqRVH
        9
    EPr2hh6LADQWqRVH  
       2022-01-26 08:29:39 +08:00 via Android
    这个你自己都说了 async await 里面有一个调度器在里面,需要的就是一个中断执行的能力,你看嵌套能中断执行吗,而且执行中断之后 cpu 要交给别的任务的,这样的任务还很多
    系统里有一万任务在跑,需要来回调度,嵌套的话这调用栈怎么办

    而且我不太明白你站在哪里在思考这个问题,你是站在运行时编译器的角度在思考还是在应用程序的角度思考
    4ark
        10
    4ark  
       2022-01-26 09:54:25 +08:00 via iPhone
    可以了解一下代数效应和纤程
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3236 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:05 · PVG 22:05 · LAX 07:05 · JFK 10:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.