浏览器工作原理与实践
李兵
前盛大创新院高级研究员
立即订阅
6167 人已学习
课程目录
已完结 42 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 参透了浏览器的工作原理,你就能解决80%的前端难题
免费
宏观视角下的浏览器 (6讲)
01 | Chrome架构:仅仅打开了1个页面,为什么有4个进程?
02 | TCP协议:如何保证页面文件能被完整送达浏览器?
03 | HTTP请求流程:为什么很多站点第二次打开速度会很快?
04 | 导航流程:从输入URL到页面展示,这中间发生了什么?
05 | 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?
06 | 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
浏览器中的JavaScript执行机制 (5讲)
07 | 变量提升:JavaScript代码是按顺序执行的吗?
08 | 调用栈:为什么JavaScript代码会出现栈溢出?
09 | 块级作用域:var缺陷以及为什么要引入let和const?
10 | 作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?
11 | this:从JavaScript执行上下文的视角讲清楚this
V8工作原理 (3讲)
12 | 栈空间和堆空间:数据是如何存储的?
13 | 垃圾回收:垃圾数据是如何自动回收的?
14 | 编译器和解释器:V8是如何执行一段JavaScript代码的?
浏览器中的页面循环系统 (6讲)
15 | 消息队列和事件循环:页面是怎么“活”起来的?
16 | WebAPI:setTimeout是如何实现的?
17 | WebAPI:XMLHttpRequest是怎么实现的?
18 | 宏任务和微任务:不是所有任务都是一个待遇
19 | Promise:使用Promise,告别回调函数
20 | async/await:使用同步的方式去写异步代码
浏览器中的页面 (8讲)
21 | Chrome开发者工具:利用网络面板做性能分析
22 | DOM树:JavaScript是如何影响DOM树构建的?
23 | 渲染流水线:CSS如何影响首次加载时的白屏时间?
24 | 分层和合成机制:为什么CSS动画比JavaScript高效?
25 | 页面性能:如何系统地优化页面?
26 | 虚拟DOM:虚拟DOM和实际的DOM有何不同?
27 | 渐进式网页应用(PWA):它究竟解决了Web应用的哪些问题?
28 | WebComponent:像搭积木一样构建Web应用
浏览器中的网络 (3讲)
29 | HTTP/1:HTTP性能优化
30|HTTP/2:如何提升网络速度?
31|HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
浏览器安全 (5讲)
32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?
33 | 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
34 | CSRF攻击:陌生链接不要随便点
35 | 安全沙箱:页面和系统之间的隔离墙
36 | HTTPS:让数据传输更安全
结束语 (1讲)
结束语 | 大道至简
课外加餐 (4讲)
加餐一|浏览上下文组:如何计算Chrome中渲染进程的个数?
加餐二|任务调度:有了setTimeOut,为什么还要使用rAF?
加餐三|加载阶段性能:使用Audits来优化Web性能
加餐四|页面性能工具:如何使用Performance?
浏览器工作原理与实践
登录|注册

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

李兵 2019-09-19
上篇文章中,我们介绍了怎么使用 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《浏览器工作原理与实践》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(19)

  • 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
    8
    25
  • 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
    8
  • 许童童
    感谢老师的分享,懂了,生成器+Promise+自动迭代器=async/await。
    2019-09-19
    3
  • 你好,这还有个小疑问:
    就是foo函数被标记上async后,会隐式生成一个promise,然后在await foo()处,await本身又会生成一个promise_,这两个promise是什么关系?
    2019-09-30
    1
    2
  • bobi
    //foo 函数
    function* foo() {
        let response1 = yield fetch('https://www.geekbang.org')
        console.log('response1')
        console.log(response1) // 这里为什么是undefined
        let response2 = yield fetch('https://www.geekbang.org/test')
        console.log('response2')
        console.log(response2) // 这里为什么是undefined
    }
    老师,在你举的生成器和Promise结合的例子中,为什么执行器里面取不到接口的值啊?
    2019-09-21
    1
    1
  • 小海
    yield 'generator 2' 这段代码写错了,全部都是yield 'generator 2' 应该是1-4
    2019-11-24
  • 歌顿
    await 100 讲清楚了,但是 await async 函数没有讲清楚啊。
    比如说 foo函数被标记上async后,会隐式生成一个promise,然后在await foo()处,await本身又会生成一个promise_,这两个promise是什么关系?
    2019-10-21
    1
  • 忘忧草的约定
    老师对于'bar end'和'promise then'的顺序我还是有点疑问,既然是microtask中恢复bar协程的的执行、不应该是先执行“bar end”吗
    2019-09-26
  • 忘忧草的约定

    'script start'
    'bar start'
    'foo'
    'promise executor'
    'script end'
    'bar end'
    'promise then'
    'setTimeout'
    2019-09-26
  • 蓝配鸡
    Script then
    Bar start
    Foo
    Promise executor
    Script end
    Promise then
    Settimeout

    老师相信我真的不是跑出来的😂

    主要考查了promise,微任务, 宏任务, 还有使用async await时候协程时间的相互合作。
    2019-09-25
  • 小兵
    async function foo() {
        console.log(1)
        let a = await 100
        // 从这里开始的内容是不是可以看作then绑定的回调函数的内容啊,看图又不像,不好理解。
        console.log(a)
        console.log(2)
    }
    console.log(0)
    foo()
    console.log(3)
    2019-09-24
  • Louis Hu
    老师您好:
    想请问一下如果 await 是一个Promise的对象呢?
    这时会发生什么?
    2019-09-20
    1
  • 柒月
    async function foo() {
        console.log('foo'); //3 foo
    }
    async function bar() {
        console.log('bar start');
        await foo()
        console.log('bar end'); //6 bar end
    }
    console.log('script start');//1 script start
    setTimeout(()=>{
        console.log('setTimeout'); 7//setTimeout
    },0)
    bar();//2 bar start
    new Promise(function (resolve) {
        resolve();
    }).then(function () {
        console.log('promise then'); //5 promise then
    })
    console.log('script end');// 4 script end

    浏览器和node环境是有点不一样呢?为啥呢?
    2019-09-20
  • Geek_f74777
    老师,课后习题中的第一个异步foo函数中会异步执行返回一个Promise对象,那么这个Promise对象在创建返回的过程中,是否会往微任务队列中添加微任务?既然foo()执行返回的结果已经是一个Promise了,那么V8引擎还会将await后的foo()
    返回的Promise再进行一次Promise封装吗?
    2019-09-19
  • 李柏良
    浏览器
    script start -> bar start -> foo -> promise executor -> script end -> bar end -> promise then -> setTimeout
    在vscode bash 中 执行 node
    script start -> bar start -> foo -> promise executor -> script end -> promise then -> bar end -> setTimeout
    bar end 和 promise then 的顺序不一样
    2019-09-19
  • Luke
    之前一直都很困惑generator 的执行机制,今天终于明白了,原来是协程!感谢老师的分享!
    2019-09-19
  • oc7
    node和浏览器环境下跑的结果不一致,这是为什么?
    2019-09-19
    2
  • Geek_Jamorx
    script start
    bar start
    promise executor
    script end
    foo
    bar end
    promise then
    setTimeout
    2019-09-19
    1
  • peter
    假设进程下有三个线程:A、B、C,其中A是主线程。 那么,文中用的“主线程”是指A吗? B和C不是主线程,就不能有协程吗?
    2019-09-19
    1
收起评论
19
返回
顶部