V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
yazoox
V2EX  ›  问与答

有没有把 Promise, Generator, 和 async/await 之间的关系讲解得比较清楚条理的文章或者博客?

  •  
  •   yazoox · 2020-05-09 18:35:07 +08:00 · 3365 次点击
    这是一个创建于 1692 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果,正在学习前端开发。对这几块有点迷糊。

    感觉好像是 先有 Promise, 再有 generator,然后现在又有了 async/await

    好像越来越“先进”和“简单”。

    有没有对这三者的理解 /解析比较好的文章或者博客,想理解得更深入一些?

    谢谢!

    28 条回复    2020-05-11 09:15:25 +08:00
    woodensail
        1
    woodensail  
       2020-05-09 18:40:58 +08:00
    先有 promise,解决了回调地狱的问题。但是副作用是一段逻辑被切分到多个作用域中,且 return 和异常都被拦截。因为本质上 js 一个函数必须同步执行完毕,想实现异步只能用多个函数来实现。

    然后有了 generator,generator 的创举在于允许一个函数执行到一半挂起,等异步回调后继续执行,完美的解决了 promise 带来的副作用。

    而 async/await 本质上是 generator 在特殊场景下使用时的一个语法糖而已,省得你去依赖 co 模块了。

    至于文章手头还真没有,毕竟也是很老的东西了……
    ClericPy
        2
    ClericPy  
       2020-05-09 18:44:57 +08:00
    平时遇到问题都直接看 MDN 的, 非要找教程的话,

    现代 JavaScript 教程 - https://zh.javascript.info/

    是我之前看的, 感觉有点其他语言基础, 理解起来应该不费劲, 至于更深入... 得看想多深了
    woodensail
        3
    woodensail  
       2020-05-09 18:47:04 +08:00
    另外再说下关于语法糖的问题,我一般把 async/await 看做 generator,因为 async/await 核心的异步挂起和作用域保持功能已经在 generator 实现了,就算没有 async/await,要不了多少代码也能封装类似的功能出来。

    而 promise 到 generator 虽然也能通过 regenerator 的方式来实现,但是缺少作用域保持和异步挂起。
    只能通过套一个函数保持作用域,然后多次调用同一个方法,通过 switch case 方式来分片执行。而你的原始代码也被编译得面目全非。是个非常不优雅的 hack 手段。远不如原生的 generator 好。
    noobsheldon
        4
    noobsheldon  
       2020-05-09 19:01:54 +08:00
    Angular Observable
    kidlj
        5
    kidlj  
       2020-05-09 19:04:51 +08:00 via iPhone
    阮一峰有一系列几篇博客,从回调,函数科里化,讲到 promise,过渡期的 co 库的实现,然后到 async/await. 非常不错。
    sunjourney
        6
    sunjourney  
       2020-05-09 19:13:18 +08:00
    @woodensail #3 又是一个阮一峰文章的受害者
    helloSpringBoot
        7
    helloSpringBoot  
       2020-05-09 19:39:09 +08:00
    lizheming
        8
    lizheming  
       2020-05-09 21:09:08 +08:00 via iPhone
    CosimoZi
        9
    CosimoZi  
       2020-05-09 21:09:49 +08:00
    continuation 的不同表现形式而已.万变不离其宗.
    giuem
        10
    giuem  
       2020-05-09 21:17:42 +08:00 via iPhone
    https://zhuanlan.zhihu.com/p/83965949 看这篇文章的后半段
    JayLin1011
        11
    JayLin1011  
       2020-05-09 22:56:25 +08:00
    可以看一下阮一峰老師的教程,深入淺出異步編程=。=
    zhuowenli
        12
    zhuowenli  
       2020-05-09 22:59:43 +08:00 via Android
    《你不知道的 JavaScript 》中卷
    mxT52CRuqR6o5
        13
    mxT52CRuqR6o5  
       2020-05-10 02:56:30 +08:00 via Android
    Promide 搞明白,async/await 自然就明白了,async/await 是 promise 的语法糖
    autoxbc
        14
    autoxbc  
       2020-05-10 05:02:02 +08:00
    尽管 Babel 的 async/await 是基于生成器实现的,然而在逻辑关系上,完全可以略过生成器,从 Promise 直接到 async/await

    学习路径是
    1. 彻底了解 Promise 的工作逻辑,注意工作逻辑不是实现细节
    http://liubin.org/promises-book/

    2. async/await 完全当成 Promise 的语法糖
    async 就是在包装 Promise,await 就是解开 Promise
    ysc3839
        15
    ysc3839  
       2020-05-10 07:17:37 +08:00 via Android
    @woodensail Promise 没解决回调地狱,传递给 Promise then 的还是一个个回调函数。async function 才解决了。
    按照我的理解,Promise 是提供了一个通用的异步回调和返回值的工具库。没有 Promise 的时候,异步回调一般是把回调函数作为函数参数传递进去,不同库的接口各不相同。有了 Promise 之后,大家都可以用一个统一的接口,可以简化开发。
    ciaoly
        16
    ciaoly  
       2020-05-10 07:43:26 +08:00 via Android
    @sunjourney 何以出此言?阮一峰的博客写的不对吗?
    chengzi168
        17
    chengzi168  
       2020-05-10 08:32:22 +08:00 via Android
    @sunjourney 求解释,阮一峰的文章不对么?
    woodensail
        18
    woodensail  
       2020-05-10 14:48:46 +08:00   ❤️ 1
    @ysc3839 额,那是你用法不对,promise 的正确用法是 fun1().then(fun2).then(fun3)这样,无论多少个异步操作,只需要一层回调即可。缺点就是上面说的你没法直接 return,想 rutern 就只能抛异常,然后就把异常信息污染了。
    ysc3839
        19
    ysc3839  
       2020-05-10 15:07:21 +08:00 via Android
    @woodensail 我知道是这样一个个 then,但是还是把整个代码逻辑拆分成了一个个回调函数。
    woodensail
        20
    woodensail  
       2020-05-10 15:11:33 +08:00
    @sunjourney 那啥,不知道你为什么会这么认为,但是我确实没读过他的文章
    ysc3839
        21
    ysc3839  
       2020-05-10 15:12:42 +08:00 via Android
    @woodensail 而且不对呀,这是针对一个 Promise 的情况,假如第一个 Promise 执行成功了,要执行另一个 Promise,还是会变成那种回调嵌套的情况。
    woodensail
        22
    woodensail  
       2020-05-10 15:13:22 +08:00   ❤️ 1
    @ysc3839 是的啊,我上面也说了,promise 在解决回调地狱后也带来了作用域分裂,流程控制困难等新的问题,不是个完美的解决方案。直到 generator 诞生才彻底解决了问题。
    woodensail
        23
    woodensail  
       2020-05-10 15:14:48 +08:00   ❤️ 1
    @ysc3839 你看我写的,then 是可以链式调用的,fun1().then(fun2).then(fun3).then(fun4).then(fun5)
    只额外包了一层而已。
    不会出现回调地狱那样左边 tab 半个屏幕的情况。
    woodensail
        24
    woodensail  
       2020-05-10 15:23:55 +08:00   ❤️ 1
    @ysc3839 @ysc3839 接上面那个例子说下 promise 解决回调地狱后带来的新问题。

    1:作用域分裂,比如 fun3 中请求的得到的结果在 fun4 要使用,但是直接在 fun3 和 4 的作用域不是同一个。所以直接在 fun3 中定义变量无效,必须把变量声明提前到 fun1 执行之前。

    2:流程控制困难,比如 fun2 中请求结果显示流程不能继续执行,需要退出。
    这时候没法跳过 345 的执行,要么 fun2 通过变量或返回值来通知,在 345 的开头分别判断一下标志,有标志就直接退出。
    或者直接抛异常来跳过后面的步骤,但这样一样,业务流程就和真正的异常混在了一起,给异常治理带来了麻烦。
    ysc3839
        25
    ysc3839  
       2020-05-10 15:25:33 +08:00 via Android
    @woodensail 查了下文档,是我孤陋寡闻了。then 的回调函数可以返回一个 Promise,下一个 then 的回调就会在返回的那个 Promise 完成时被调用了。
    woodensail
        26
    woodensail  
       2020-05-10 15:37:40 +08:00
    @ysc3839 嗯,我当年刚知道 promise 这个特性的时候非常兴奋,然后很快就被业务教做人了。
    一个医保的业务,需要
    1:访问自家服务器获取订单信息(包含多个账单)
    2:访问医保服务器做预结算
    4:循环访问医保服务器,一个一个创建账单
    3:访问自家服务器记录账单
    5:循环访问医保服务器,一个一个确认账单
    5:访问自家服务记录结果
    其中任何一步失败都要跳过后续步骤,并且记录失败信息。如果失败时已经有部分账单被创建需要进行取消;如果有部分账单已经确认,需要进行退费。

    最后用皮肉面色写出来一坨屎山,太绝望了,过去这么多年,我都再也没碰到过这大的屎山了
    yazoox
        27
    yazoox  
    OP
       2020-05-11 08:41:58 +08:00
    @woodensail 兄弟,既然你觉得 Promise 不能很好处理这个“医保”问题。
    时隔多年,现在有了 async/await 了,能否很漂亮的解决当年的这个问题呢?

    我觉得这个问题很值得花点时间研究一下。

    如果你有空的话,可能分享给我们大家。(别忘记了 @我一下)
    woodensail
        28
    woodensail  
       2020-05-11 09:15:25 +08:00
    @yazoox 后来有想过,不过已经跳槽了也就不管了。如果用 async 的话有几个优势,首先整个流程在同一个方法中,可以随时 return 退出后续步骤,在扫尾处理方面会简单很多。
    另外由于 async 中可以直接 for 循环执行异步语句,所以不需要用 promise 迭代的方式来顺序执行数组中的任务,也让代码可读性大幅提高。

    整体的话估计开发难度不到原来的一半,代码量下降不多但是可读性大幅提升。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1075 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:16 · PVG 03:16 · LAX 11:16 · JFK 14:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.