首页 > 软件开发 > JavaScript >

ES6之async+await 同步/异步方案

来源:互联网 2023-03-16 23:57:33 版权归原作者所有,如有侵权,请联系我们

异步编程一直是JavaScript 编程的重大事项。关于异步方案, ES6 先是出现了 基于状态管理的 Promise,然后出现了 Generator 函数 co 函数,紧接着又出现了 ES7 的 async await 方案。9Bn办公区 - 实用经验教程分享!

本文力求以最简明的方式来疏通 async await。9Bn办公区 - 实用经验教程分享!

工具/原料

  • JavaScript

方法/步骤

  • 1

    异步编程的几个场景9Bn办公区 - 实用经验教程分享!

    先从一个常见问题开始:一个for 循环中,如何异步的打印迭代顺序9Bn办公区 - 实用经验教程分享!

    用闭包或者ES6 规定的 let 块级作用域来回答这个问题。9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 2

    这里描述的是一个均匀发生的的异步,被依次按既定的顺序排在异步队列中等待执行。9Bn办公区 - 实用经验教程分享!

    如果异步不是均匀发生的,那么被注册在异步队列中的顺序就是乱序的。9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 3

    返回的结果是乱序不可控的,这本来就是最为真实的异步。另一种情况是,在循环中,如果希望前一个异步执行完毕、后一个异步再执行,该怎么办?9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 4

    这不就是多个异步 “串行”9Bn办公区 - 实用经验教程分享!

    在回调 callback 嵌套异步操作、再回调的方式,不就解决了这个问题!或者,使用 Promise then() 层层嵌套同样也能解决问题。但是,如果硬是要将这种嵌套的方式写在循环中,还恐怕还需费一番周折。9Bn办公区 - 实用经验教程分享!

    异步同步化方案9Bn办公区 - 实用经验教程分享!

    如果要去将一批数据发送到服务器,只有前一批发送成功(即服务器返回成功的响应),才开始下一批数据的发送,否则终止发送。这就是一个典型的 “for 循环中存在相互依赖的异步操作” 的例子。9Bn办公区 - 实用经验教程分享!

    明显,这种 “串行” 的异步,实质上可以当成同步。它和乱序的异步比较起来,花费了更多的时间。按理说,我们希望程序异步执行,就是为了 “跳过” 阻塞,较少时间花销。但与之相反的是,如果需要一系列的异步 “串行”,我们应该怎样很好的进行编程?9Bn办公区 - 实用经验教程分享!

    对于这个 “串行” 异步,有了 ES6 就非常容易的解决了这个问题。9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 5

    本次循环,等有了结果,再进行下一次循环。因此,循环每执行一次就会被暂停(“卡住”)一次,直到循环结束。这种编码实现,很好的消除了层层嵌套的 “回调地狱” 问题,降低了认知难度。9Bn办公区 - 实用经验教程分享!

    这就是异步问题同步化的方案。如果说 Promise 主要解决的是异步回调问题,那么 async await 主要解决的就是将异步问题同步化,降低异步编程的认知负担。9Bn办公区 - 实用经验教程分享!

    async await “外异内同”9Bn办公区 - 实用经验教程分享!

    早先接触这套 API 时,看着繁琐的文档,一知半解的认为 async await 主要用来解决异步问题同步化的。9Bn办公区 - 实用经验教程分享!

    其实不然。从上面的例子看到:async 关键字声明了一个 异步函数,这个 异步函数 体内有一行 await 语句,它告示了该行为同步执行,并且与上下相邻的代码是依次逐行执行的。9Bn办公区 - 实用经验教程分享!

    将这个形式化的东西再翻译一下,就是:9Bn办公区 - 实用经验教程分享!

    1、async 函数执行后,总是返回了一个 promise 对象2、await 所在的那一行语句是同步的9Bn办公区 - 实用经验教程分享!

    其中,1 说明了从外部看,task 方法执行后返回一个 Promise 对象,正因为它返回的是 Promise,所以可以理解task 是一个异步方法。毫无疑问它是这样用的:9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 6

    2说明了在 task 函数内部,异步已经被 “削” 成了同步。整个就是一个执行稍微耗时的函数而已。9Bn办公区 - 实用经验教程分享!

    综合 1、2,从形式上看,就是 “task 整体是一个异步函数,内部整个是同步的”,简称“外异内同”。9Bn办公区 - 实用经验教程分享!

    整体是一个异步函数 不难理解。在实现上,我们不妨逆向一下,语言层面让async关键字调用时,在函数执行的末尾强制增加一个promise 反回:9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 7

    实际上 await 调用,是让后边的语句(函数)做了一个递归执行,直到获取到结果并使其 状态 变更,才会 resolve 掉,而只有 resolve 掉,await 那一行代码才算执行完,才继续往下一行执行。所以,尽管外部是一个大大的 for 循环,但是整个 for 循环是依次串行的。9Bn办公区 - 实用经验教程分享!

    因此,仅从上述框架的外观出发,就不难理解 async await 的意义。使用起来也就这么简单,反而 Promise 是一个必须掌握的基础件。9Bn办公区 - 实用经验教程分享!

    秉承本次《重读 ES6》系列的原则,不过多追求理解细节和具体实现过程。继续巩固一下这个 “形式化” 的理解。9Bn办公区 - 实用经验教程分享!

    async await 的进一步理解9Bn办公区 - 实用经验教程分享!

    有这样的一个异步操作 longTimeTask,已经用 Promise 进行了包装。借助该函数进行一系列验证。9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 8

    以上 2 步执行,清晰的证明了 exec1 函数体内是同步、逐行逐行执行的,即先执行完异步操作,然后进行 console.log() 打印。而 exec1() 的执行结果就直接是一个 Promise,因为它最先会蹦出来一串 Promise ...,然后才是 exec1 函数的内部执行日志。9Bn办公区 - 实用经验教程分享!

    因此,所有验证,完全符合 整体是一个异步函数,内部整个是同步的 的总结。9Bn办公区 - 实用经验教程分享!

    await 如何执行其后语句9Bn办公区 - 实用经验教程分享!

    回到 await ,看看它是如何执行其后边的语句的。假设:让 longTimeTask() 后边直接带 then() 回调,分两种情况:9Bn办公区 - 实用经验教程分享!

    1)then() 中不再返回任何东西2) then() 中继续手动返回另一个 promise9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 8该信息未经授权抓取自百度经验
  • 9

    longTimeTask() 加上再多得 then() 回调,也不过是放在了它的回调列队 queue 里了。也就是说,await 命令之后始终是一条 表达式语句,只不过上述代码书写方式比较让人迷惑。(比较好的实践建议是,将 longTimeTask 方法身后的 then() 移入 longTimeTask 函数体封装起来)9Bn办公区 - 实用经验教程分享!

    其次,手动返回另一个 promise 和什么也不返回,关系到 longTimeTask() 方法最终 resolve 出去的内容不一样。换句话说,await 命令会提取其后边的promise 的 resolve 结果,进而直接导致 result 的不同。9Bn办公区 - 实用经验教程分享!

    值得强调的是,await 命令只认 resolve 结果,对 reject 结果报错。不妨用以下的 return 语句替换上述 return 进行验证。9Bn办公区 - 实用经验教程分享!

    ES6之async await 同步/异步方案9Bn办公区 - 实用经验教程分享!

  • 以上方法由办公区教程网编辑摘抄自百度经验可供大家参考!9Bn办公区 - 实用经验教程分享!


    标签: JAVASCRIPT

    办公区 Copyright © 2016-2023 www.bgqu.net. Some Rights Reserved. 备案号:湘ICP备2020019561号