浏览器工作原理与实践
李兵
前盛大创新院高级研究员
立即订阅
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?
浏览器工作原理与实践
登录|注册

15 | 消息队列和事件循环:页面是怎么“活”起来的?

李兵 2019-09-07
前面我们讲到了每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是我们今天要讲的消息队列和事件循环系统。
在写这篇文章之前,我翻阅了大量的资料,却发现没有一篇文章能把消息循环系统给讲清楚的,所以我决定用一篇文章来专门介绍页面的事件循环系统。事件循环非常底层且非常重要,学会它能让你理解页面到底是如何运行的, 所以在本篇文章中,我们会将页面的事件循环给梳理清楚、讲透彻。
为了能让你更加深刻地理解事件循环机制,我们就从最简单的场景来分析,然后带你一步步了解浏览器页面主线程是如何运作的。
需要说明的是,文章中的代码我会采用 C++ 来示范。如果你不熟悉 C++,也没有关系,这里并没有涉及到任何复杂的知识点,只要你了解 JavaScript 或 Python,你就会看懂。

使用单线程处理安排好的任务

我们先从最简单的场景讲起,比如有如下一系列的任务:
任务 1:1+2
任务 2:20/5
任务 3:7*8
任务 4:打印出任务 1、任务 2、任务 3 的运算结果
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《浏览器工作原理与实践》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(38)

  • 易儿易
    宏任务是开会分配的工作内容,微任务是工作过程中被临时安排的内容,可以这么比喻吗?

    作者回复: 这个比喻形象

    2019-09-08
    21
  • 肥嘟嘟左卫门
    老师,我感觉最近学起来很迷茫。我本身是非计算机专业的,转行前端将近一年,也能照搬代码去干活,但是总感觉很多东西不通,整个就是一个闭塞的状态,于是我把今年的目标定为恶补计算机基础知识,然后我就找计算机网络方面的知识,浏览器方面的,也包括您的这个专栏,并且也结合着极客时间另外一个关于http的专栏,感觉看的时候好像是懂了(其实我也知道也只是停留在我这个知识层面的“懂了”),会有那种原来平时工作时候他们说的那些概念是这样的意思,也会觉得曾经觉得很难得东西,一下子就通了的感觉,但是另一边我又发现一个问题就是看到现在,像之前专栏讲的我就忘了,平时打通的点也都没有太多印象。我有点迷茫了,我不知道是不是因为我实操太少了,平时遇到的问题太少,所以在学这些的时候会印象不深刻。难道我现在应该把更多的精力放在框架的使用,不停的写代码,而不是基础上吗?

    作者回复: 首先我的观点是工作和知识体系的构建都是重要的,这两者向铺相成。

    所以学习过程也是停不下来的,基础知识的学习不要间断。

    工作是实践的好机会,但是在工作中你涉及到内容通常会限定在一个很窄的领域,要想通过工作拓宽自己的知识边界,那该如何突破呢?

    我是这样做:

    把工作中的项目看成是一个探险游戏,游戏中有你熟悉的领域,也有你不熟悉的领域,通常你所做的工作都是在你最熟悉的领域。

    不过我还会做另外一件事,就是把游戏地图画出来,画地图的过程也就是全面熟悉项目架构的过程,其中可能涉及到很多你不熟悉的领域,然后你要做的事逐步拓宽这张地图!

    当然人的精力是有限的,所以搭建知识架构很重要,然后再找几个领域深耕。

    2019-09-09
    11
  • mfist
    微任务的本质结合消息队列和事件循环我理解:当事件循环接受到消息时候,判断是否是优先级高的任务,选择插入消息队列的位置不同,进而影响消息执行的顺序。
    很期待通过js回调方式解决一次执行很长js带来的页面卡顿的问题。

    今日总结
    为了应对渲染进程主线程繁琐的任务(DOM解析、样式计算、布局、处理js任务、各种输入事件),引入了消息队列和事件循环系统。
    从任务的复杂度逐渐增加,循序渐进的分析每种场景的处理方式。
    1. 单线程处理安排好的同步任务
    2. 引入事件循环接受新的任务
    3. 引入消息队列处理其他进程发来的任务
    4. 引入宏任务和微任务解决任务优先级的问题
    5. 通过Js回调功能解决单个js任务执行时间过长的问题。

    作者回复: 你对微任务的理解还是有些偏差的!

    每个宏任务都有一个微任务列表,在宏任务的执行过程中产生微任务会被添加到改列表中,等宏任务快执行结束之后,会执行微认为列表,所以微任务依然运行在当前宏任务的执行环境中,这个特性会导致宏任务和微任务有一些本质上的区别!我们后面再介绍,你可以重点关注下。

    2019-09-07
    1
    10
  • 阿桐
    老师,为什么说页面是单线程架构?

    默认情况下每个标签页都会配套一个渲染进程,而一个渲染进程里不是有主线程、合成线程、IO线程等多个线程吗

    是因为【排版引擎 blink】 和【JavaScript引擎 v8】都工作在渲染进程的主线程上并且是互斥的,基于这点说页面是单线程架构?

    作者回复: 是的,他们都是在渲染进程的主线程上工作,所以同时只能执行一个。

    比如v8除了在主线程上执行JavaScript代码之外,还会在主线程上执行垃圾回收,所以执行垃圾回收时停止主线程上的所有任务,我们把垃圾回收这个特性叫着全停顿。

    2019-09-22
    4
  • 拖鞋
    老师请教个问题 用CSS3实现动画是不是不会影响主线程,和用JS实现动画会影响主线程,这个说法对么

    作者回复: 是这样的,部分css3的动画效果是在合成线程上实现的,不需要主线程介入,所以省去了重拍和重绘的过程,这就大大提升了渲染效率。

    JavaScript都是在在主线程上执行的,所以JavaScript的动画需要主线程的参与,所以效率会大打折扣!

    2019-09-16
    4
  • GY
    非常疑惑,几个问题希望能解答一下
    1.我在查阅一些资料,是这么描述的,渲染进程主要有GUI渲染渲染线程,JS引擎线程,事件触发线程,定时触发器线程。而本文一直在讲主线程,那么主线程到底是什么?是独立于这几个线程吗,还是这几个线程中的某一个
    2.您在回答提问中,有提到了IO线程,以及辅助线程,这些又是什么?我在网上查不到比较好的回答
    3.比较好奇window.onload的是如何执行的,js引擎执行到这段代码时,不会立即执行,那么会将这段代码怎么处理,什么时候执行,当他被触发时,是作为宏任务进入消息队列还是微任务进入微任务队列,还是其他的方式?
    4.我该如何理解任务,因为我在测试一个HTML文件中出现多个script标签且都包含一段setTimeout,前面的script中的setTimeout函数,有时会先于后面的script块执行,那么我应该怎么理解这个过程,他并不是按照 第一个script标签中代码编译--执行--setTimeout进入消息队列--微任务执行--第二个script标签编译--执行--setTimeout进入消息队列--微任务执行.....--从消息队列取出第1个script标签中的setTImeout执行--从消息队列取出第2个script标签中的setTImeout执行
    2019-09-25
    9
    3
  • Rapheal
    老师,可以请问下:渲染进程的主线程和V8执行机主线程是同一个线程吗?一个渲染进程有几个线程,分别有啥作用?

    作者回复: 主要有IO线程,用开负责和其它进程IPC通信的,然后主线程主要跑页面的!

    V8是在主线程上执行的,因为dom操作啥的都是在主线程上执行的。

    当然还有其它很多辅助线程,比如预解析DOM的线程,垃圾回收也有一些辅助线程。

    2019-09-08
    3
  • 瞧,这个人
    宿主发起的任务是宏任务 如点击事件,settimeout 进消息队列;js引擎发起的任务是微任务如promise
    2019-09-08
    3
  • 得闲读书
    老师,所以,事件循环其实是监听执行任务的循环机制吗?而每一个执行任务都存档在消息队列里面,这些统称为宏任务,微任务是执行宏任务中遇到的异步操作吧,就是异步代码,如promise,settimeout任务。执行宏任务遇到异步任务先将其放入微任务列表,等该宏任务执行一遍后再执行该宏任务的微任务列表,我这样理解对吗?

    作者回复: 第一个理解没错,事件循环系统就是在监听并执行消息队列中的任务!

    第二个理解也没问题,不过promise触发的微任务,settimeout触发的是宏任务!

    2019-09-07
    3
  • 阿桐
    老师,专栏中有段内容我看了几遍还是似懂非懂,您方便举个例子再给我说说吗?

    “比如一个典型的场景是监控 DOM 节点的变化情况(节点的插入、修改、删除等动态变化),然后根据这些变化来处理相应的业务逻辑。一个通用的设计的是,利用 JavaScript 设计一套监听接口,当变化发生时,渲染引擎同步调用这些接口,这是一个典型的观察者模式。
    不过这个模式有个问题,因为 DOM 变化非常频繁,如果每次发生变化的时候,都直接调用相应的 JavaScript 接口,那么这个当前的任务执行时间会被拉长,从而导致执行效率的下降。”

    作者回复: 这个我会在微任务那节详细分析

    2019-09-07
    1
    2
  • splm
    疑问:宏任务与微任务的加入是为了实时性和效率。按照策略,微任务的执行是在宏任务主要功能执行完毕后调用,也就是说优先于下一个任务,但仍会因为宏任务正在执行主要功能而有存在延迟?如果宏任务的主要功能相对耗时,那是不是也意味着微任务也要等待,那实时性又是怎么确定的?
    2019-10-15
    1
    1
  • L
    老师我觉得在第一篇那个进程哪里,应该可以顺手说一下浏览器主进程中的那些线程。。。

    作者回复: 等专栏结束后起来补充介绍。几个主要线程的架构

    2019-10-14
    1
  • 蔡金标
    老师您好,我想请问一下,这里说的主线程是指JS引擎线程吗,如果是的话,所以前面章节讲的构建DOM树那些都是在JS引擎线程上的而不是GUI渲染线程对吗
    2019-09-16
    1
  • AICC
    老师,有一个疑问,我理解一个tab标签页是在任务管理器是被视为一个进程,那在这样的进程内,理论上都应该是线程,比如一个tab页里分为:GUI线程,JS引擎线程,事件触发线程,定时器线程,网络线程,而不是像文中提到的网络进程,浏览器进程?
    2019-09-13
    1
  • 朱维娜🍍
    在渲染进程里面,除了I/O线程,其他线程也会往消息队列中添加任务,是吗?

    作者回复: 有啊,比如渲染过程就有合成线程,解析DOM过程中还有预解析线程,这些现场都会和主线程有交互的

    2019-09-11
    1
  • Geek_Jamorx
    切入角度很好,通俗易懂。微任务就是优先级最高的消息队列,用于在当前函数执行结束后立即执行
    有个问题,宏任务队列和微任务队列分别只有一个么,还是宏任务队列中又有setTimeout队列和setInterval队列?希望解答一下

    作者回复: 这个在下篇文章揭晓

    2019-09-07
    1
  • 早起不吃虫
    老师,宏任务跟微任务能不能详细讲讲呢,譬如哪些是宏任务哪些是微任务?

    作者回复: 马上会有专门的章节来讲微认为,不过理解消息队列和事件循环是理解微任务的基础!

    2019-09-07
    1
  • 古小静
    微任务可以理解为一个宏任务的对应队列,在执行完宏任务之后进行该任务对应微任务的执行,如果按照老师说的,微任务中存的是DOM的操作,这样会免去很多无用的DOM的操作,对性能来说也是一种提升。可以这样理解吗?
    2019-11-29
  • A6六个周
    由于是多个线程操作同一个消息队列,所以在添加任务和取出任务时还会加上一个同步锁。
    请问老师,JS执行不是单线程的吗?为什么这里会说是由多个线程操作同一个队列?

    作者回复: 这里提到的任务是指浏览器所以需要处理的任务!

    浏览器是基于多进程+多线程架构的,所以多进程通讯(IPC)和多线程同步的问题!

    因为JavaScript引擎是运行在渲染进程的主线程上的,所以我们说JavaScript是单线程执行的!

    2019-11-25
  • Allen_
    宏观任务就像上级为一件事情调用人力物力财力,而微观任务就像利用这些人力物力财力来做一些变化,并且这些微观任务也有对应的顺序。做一件工程的时候,不可能让上级一直给你资源然后你啥都不做吧,所以得在每一次宏观任务下来后做对应的一些微观任务,并且这些微观任务也是先进先出,这样就不会一下子block掉渲染进程了。这样在页面上才有流畅的感觉和保证了及时性。
    2019-10-24
收起评论
38
返回
顶部