浏览器工作原理与实践
李兵
前盛大创新院高级研究员
56402 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 46 讲
浏览器工作原理与实践
15
15
1.0x
00:00/00:00
登录|注册

20 | async/await:使用同步的方式去写异步代码

协程
代码执行分析
C#
Dart
Python
主流编程风格
革新
语法层面包装
V8引擎
生成器
同步代码风格
回调地狱问题
then方法
编程模型
思考时间
其他语言
async/await
Promise
async/await

该思维导图由 AI 生成,仅供参考

上篇文章中,我们介绍了怎么使用 Promise 来实现回调操作,使用 Promise 能很好地解决回调地狱的问题,但是这种方式充满了 Promise 的 then() 方法,如果处理流程比较复杂的话,那么整段代码将充斥着 then,语义化不明显,代码不能很好地表示执行流程。
比如下面这样一个实际的使用场景:我先请求极客邦的内容,等返回信息之后,我再请求极客邦的另外一个资源。下面代码展示的是使用 fetch 来实现这样的需求,fetch 被定义在 window 对象中,可以用它来发起对远程资源的请求,该方法返回的是一个 Promise 对象,这和我们上篇文章中讲的 XFetch 很像,只不过 fetch 是浏览器原生支持的,并有没利用 XMLHttpRequest 来封装。
fetch('https://www.geekbang.org')
.then((response) => {
console.log(response)
return fetch('https://www.geekbang.org/test')
}).then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
})
从这段 Promise 代码可以看出来,使用 promise.then 也是相当复杂,虽然整个请求流程已经线性化了,但是代码里面包含了大量的 then 函数,使得代码依然不是太容易阅读。基于这个原因,ES7 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。你可以参考下面这段代码:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

JavaScript中的async/await是一种重大的异步编程改进,它提供了使用同步代码实现异步访问资源的能力,使代码逻辑更加清晰。通过async/await,可以使用同步的方式来写异步代码,支持try catch来捕获异常,符合人的线性思维。本文深入讲解了async/await的底层技术点,带领读者逐步了解async/await是如何工作的。文章涵盖了JavaScript异步编程的重要知识点,对于想深入了解async/await的读者具有很高的参考价值。async/await技术背后的秘密就是Promise和生成器应用,往低层说就是微任务和协程应用。使用async/await可以实现用同步代码的风格来编写异步代码,这是因为async/await的基础技术使用了生成器和Promise,生成器是协程的实现,利用生成器能实现生成器函数的暂停和恢复。V8引擎还为async/await做了大量的语法层面包装,所以了解隐藏在背后的代码有助于加深对async/await的理解。async/await无疑是异步编程领域非常大的一个革新,也是未来的一个主流的编程风格。文章还提到了其他语言引入了async/await,使用它不仅能让代码更加整洁美观,而且还能确保该函数始终都能返回Promise。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《浏览器工作原理与实践》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(71)

  • 最新
  • 精选
  • mfist
    1. 首先在主协程中初始化异步函数foo和bar,碰到console.log打印script start; 2. 解析到setTimeout,初始化一个Timer,创建一个新的task 3. 执行bar函数,将控制权交给协程,输出bar start,碰到await,执行foo,输出foo,创建一个 Promise返回给主协程 4. 将返回的promise添加到微任务队列,向下执行 new Promise,输出 promise executor,返回resolve 添加到微任务队列 5. 输出script end 6. 当前task结束之前检查微任务队列,执行第一个微任务,将控制器交给协程输出bar end 7. 执行第二个微任务 输出 promise then 8. 当前任务执行完毕进入下一个任务,输出setTimeout
    2019-09-19
    20
    118
  • Luke
    generator 函数是如何暂停执行程序的? 答案是通过协程来控制程序执行。 generator 函数是一个生成器,执行它会返回一个迭代器,这个迭代器同时也是一个协程。一个线程中可以有多个协程,但是同时只能有一个协程在执行。线程的执行是在内核态,是由操作系统来控制;协程的执行是在用户态,是完全由程序来进行控制,通过调用生成器的next()方法可以让该协程执行,通过yield关键字可以让该协程暂停,交出主线程控制权,通过return 关键字可以让该协程结束。协程切换是在用户态执行,而线程切换时需要从用户态切换到内核态,在内核态进行调度,协程相对于线程来说更加轻量、高效。 async function实现原理? async function 是通过 promise + generator 来实现的。generator 是通过协程来控制程序调度的。 ​在协程中执行异步任务时,先用promise封装该异步任务,如果异步任务完成,会将其结果放入微任务队列中,然后通过yield 让出主线程执行权,继续执行主线程js,主线程js执行完毕后,会去扫描微任务队列,如果有任务则取出任务进行执行,这时通过调用迭代器的next(result)方法,并传入任务执行结果result,将主线程执行权转交给该协程继续执行,并且将result赋值给yield 表达式左边的变量,从而以同步的方式实现了异步编程。 所以说到底async function 还是通过协程+微任务+浏览器事件循环机制来实现的。
    2019-09-19
    1
    38
  • EmilyLucky
    1.首先执行console.log('script start');打印出script start 2.接着遇到定时器,创建一个新任务,放在延迟队列中 3.紧接着执行bar函数,由于bar函数被async标记的,所以进入该函数时,JS引擎会保存当前调用栈等信息,然后执行bar函数中的console.log('bar start');语句,打印bar start。 4.接下来执行到bar函数中的await foo();语句,执行foo函数,也由于foo函数被async标记的,所以进入该函数时,JS引擎会保存当前调用栈等信息,然后执行foo函数中的console.log('foo');语句,打印foo。 5.执行到await foo()时,会默认创建一个Promise对象 6.在创建Promise对象过程中,调用了resolve()函数,且JS引擎将该任务交给微任务队列 7.然后JS引擎会暂停当前协程的执行,将主线程的控制权交给父协程,同时将创建的Promise对象返回给父协程 8.主线程的控制权交给父协程后,父协程就调用该Promise对象的then()方法监控该Promise对象的状态改变 9.接下来继续父协程的流程,执行new Promise(),打印输出promise executor,其中调用了 resolve 函数,JS引擎将该任务添加到微任务队列队尾 10.继续执行父协程上的流程,执行console.log('script end');,打印出来script end 11.随后父协程将执行结束,在结束前,会进入微任务检查点,然后执行微任务队列,微任务队列中有两个微任务等待执行,先执行第一个微任务,触发第一个promise.then()中的回调函数,将主线程的控制权交给bar函数的协程,bar函数的协程激活后,继续执行后续语句,执行 console.log('bar end');,打印输出bar end 12.bar函数协程执行完成后,执行微任务队列中的第二个微任务,触发第二个promise.then()中的回调函数,该回调函数被激活后,执行console.log('promise then');,打印输出promise then 13.执行完之后,将控制权归还给主线程,当前任务执行完毕,取出延迟队列中的任务,执行console.log('setTimeout');,打印输出setTimeout。 故:最终输出顺序是:script start => bar start => foo => promise executor => script end => bar end => promise then => setTimeout
    2020-05-17
    6
    32
  • 许童童
    感谢老师的分享,懂了,生成器+Promise+自动迭代器=async/await。
    2019-09-19
    10
  • 穿秋裤的男孩
    以前一直以为promise.then就是添加微任务,原来真的的微任务是promise.resolve/reject。then函数只是resolve/reject执行的副产品
    2020-04-17
    8
    9
  • 你好,这还有个小疑问: 就是foo函数被标记上async后,会隐式生成一个promise,然后在await foo()处,await本身又会生成一个promise_,这两个promise是什么关系?
    2019-09-30
    6
    8
  • 墨灵
    直到现在才构建起一个比较完整的异步编程范式的体系,真是深入浅出。
    2020-03-27
    6
  • 啊哈哈
    那也就是说,await之后的行为,全部作为promise.then()的微任务加入进微任务队列中了。最后在本轮宏任务执行完成后才执行当前宏任务下的微任务队列。
    2021-01-14
    2
    4
  • A LETTER
    awiat会向父协程传递创建的promise_,并执行resolve(result),该方法会被放入微任务队列中执行,之后在赋值前通过yield跳出该协程,转到父协程,然后父协程通过调用promise_.then方法来监听这个promise的变化,当父协程执行结束之前,到达检查点,去执行微任务队列时,执行到之前注册的resolve(result)时,会调用之前then注册的回调函数,在该回调函数中通过next(result)来进入子协程,并将result的值,赋值给await等式左边的变量,然后继续执行该子协程的代码,就相当于在之前的then中注册的回调函数里执行一样,实现了同步的方式来进行异步操作。
    2021-10-06
    2
  • 王玄
    老师 可以否按照mfist这种流程详细讲解一下练习题
    2021-04-19
    2
收起评论
显示
设置
留言
71
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部