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

16 | WebAPI:setTimeout是如何实现的?

李兵 2019-09-10
上一篇文章中我们介绍了页面中的事件和消息队列,知道了浏览器页面是由消息队列和事件循环系统来驱动的
那在接下来的两篇文章中,我会通过 setTimeoutXMLHttpRequest 这两个 WebAPI 来介绍事件循环的应用。这两个 WebAPI 是两种不同类型的应用,比较典型,并且在 JavaScript 中的使用频率非常高。你可能觉得它们太简单、太基础,但有时候恰恰是基础简单的东西才最重要,了解它们是如何工作的会有助于你写出更加高效的前端代码。
本篇文章主要介绍的是 setTimeout。其实说起 setTimeout 方法,从事开发的同学想必都不会陌生,它就是一个定时器,用来指定某个函数在多少毫秒之后执行。它会返回一个整数,表示定时器的编号,同时你还可以通过该编号来取消这个定时器。下面的示例代码就演示了定时器最基础的使用方式:
function showName(){
console.log("极客时间")
}
var timerID = setTimeout(showName,200);
执行上述代码,输出的结果也很明显,通过 setTimeout 指定在 200 毫秒之后调用 showName 函数,并输出“极客时间”四个字。
简单了解了 setTimeout 的使用方法后,那接下来我们就来看看浏览器是如何实现定时器的,然后再介绍下定时器在使用过程中的一些注意事项。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《浏览器工作原理与实践》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(31)

  • pyhhou
    使用 requestAnimationFrame 不需要设置具体的时间,由系统来决定回调函数的执行时间,requestAnimationFrame 里面的回调函数是在页面刷新之前执行,它跟着屏幕的刷新频率走,保证每个刷新间隔只执行一次,内如果页面未激活的话,requestAnimationFrame 也会停止渲染,这样既可以保证页面的流畅性,又能节省主线程执行函数的开销
    2019-09-10
    1
    16
  • mfist
    requestAnimationFrame 提供一个原生的API去执行动画的效果,它会在一帧(一般是16ms)间隔内根据选择浏览器情况去执行相关动作。
    setTimeout是在特定的时间间隔去执行任务,不到时间间隔不会去执行,这样浏览器就没有办法去自动优化。

    今日得到
    浏览器的页面是通过消息队列和事件循环系统来驱动的。settimeout的函数会被加入到延迟消息队列中,
    等到执行完Task任务之后就会执行延迟队列中的任务。然后分析几种场景下面的setimeout的执行方式。
    1. 如果执行一个很耗时的任务,会影响延迟消息队列中任务的执行
    2. 存在嵌套带调用时候,系统会设置最短时间间隔为4s(超过5层)
    3. 未激活的页面,setTimeout最小时间间隔为1000ms
    4. 延时执行时间的最大值2147483647,溢出会导致定时器立即执行
    5. setTimeout设置回调函数this会是回调时候对应的this对象,可以使用箭头函数解决

    作者回复: 回答的很棒,raf是按照系统刷新的节奏调用的!

    2019-09-11
    12
  • Angus
    我没有太理解这个异步延迟队列,既然是队列,但好像完全不符合先进先出的特点。在每次执行完任务队列中的一个任务之后都会去执行那些已经到期的延迟任务,这些延迟的任务具体是如何取出的呢。

    作者回复: 我文章说是队列,为了和消息队列统一起来,不然表述起来有点拗口。

    其实是一个hashmap结构,等到执行这个结构的时候,会计算hashmap中的每个任务是否到期了,到期了就去执行,直到所有到期的任务都执行结束,才会进入下一轮循环!

    2019-09-10
    1
    12
  • Helios
    请问老师不是说settimeout属于宏任务不,不应该属于在上节课讲的消息队列中么
    怎么这次有跑到延时队列中了呢,这两个队列有什么关系呢,延时队列也分宏任务和微任务?
    2019-09-11
    2
    7
  • l1shu
    为什么有些文章说渲染进程中有一个定时器线程用来计时的 到时间后会把回调函数塞到消息队列 而没有提到延迟队列这个说法 求老师解答
    2019-10-22
    5
  • moss
    这一节学习到了不少setTimeout的知识。不过关于消息队列我有不同的理解。
    1. 关于任务优先级。whatwg标准里,“An event loop has one or more task queues”。消息队列其实不算是队列,因为有很多个task queue。“a task queue is a set of tasks”。每一个task queue才是一个队列。而对于每一个task queue里的task,其task source是一致的,或者说不同的task source会被推入到不同的task queue。就是规范里说的“every task source must be associated with a specific task queue”。而task sources都有哪些呢?比如DOM操作,UI事件,网络事件等。这个setTimout应该也算是一种task source吧?会放到专门的队列里。上一轮事件循环结束后,会先选择一个高优先级的task queue,然后取出task queue的第一个task,也因此而有了事件的优先级,老师将的延时队列我有点不太知道怎么融入我现有的知识体系。
    2. “重新布局”是task吗?
    老师说“重新布局”的事件会被放到消息队列。我的理解是task -> microtask -> update the rendering。当然不是每次循环都走渲染过程,因为每次循环都特别快不可能每次都走一次渲染,浏览器会遵循17ms一桢的原则走一次update the rendering,其中rAF也在此阶段执行,也是老师题目里rAF更流畅的原因。而重新布局也是在update the rendering阶段执行的,resize和onscroll都是在update the rendering阶段。标准里在update the rendering阶段,会有“run the resize steps”,“run the scroll steps”,这也是为啥scrolling自带节流效果最多17ms触发一次回调的原因,所以我认为连续事件(resize,scroll)既然都不是task -> microtask -> update the rendering里的task,而是update the rendering阶段,应该不会推送到某一个task queue才对。

    作者回复: 第一个问题我在18节也回答过了,一个是标准,一个是实现,标准定义了很多队列,而浏览器只实现了一个普通队列和一个延时队列!

    第二个问题,你说的那个update the randering就是说rAF吧?rAF是用户调用的,重新布局是渲染引擎自动安排的任务,必然要放到消息队列中!

    滚动一般默认都是在合成线程里面完成的,这种都没用到主线程

    2019-09-18
    1
    4
  • 老师,你好。
    请问微任务的执行是在延迟队列任务执行之前吗?

    作者回复: 没有之前之后啊,延时队列里面是宏任务,普通的消息队列里面也是宏任务,微任务是在宏任务快要执行结束之前执行的!

    2019-09-17
    3
  • 李懂
    1.执行延迟队列的任务,是一次循环只取出一个,还是检查只要时间到了,就执行?
    2.微任务是在宏任务里的,是执行完一个宏任务,就去执行宏任务里面的微任务?

    作者回复: 比如有五个定时的任务到期了,那么会分别把这个五个定时器的任务执行掉,再开始下次循环过程!

    chromium中,当执行一个宏任务时,才会创建微任务队列,等遇到checkpoint时就会执行微任务!

    2019-09-10
    2
  • Wlt
    老师,您好,延迟队列和消息队列是什么关系,怎么配合工作的?

    作者回复: 延迟消息队列主要是放一些定时执行的任务,如JavaScript设置定时器的回调,还有浏览器内部的一些定时回调任务! 这类任务需要等到指定时间间隔之后才会被执行!

    而正常的消息队列中的任务只会按照顺序执行,执行完上个任务接着执行下个任务,不需要关系时间间隔!

    2019-10-28
    1
  • Djan Unchained
    setTimeout是在普通队列的当前一个任务结束之后,才去延迟队列查询并执行到期任务?还是在本轮事件循环的所有同步任务执行完?另外,怎么确定一轮事件循环是从哪儿到哪儿?
    2019-10-25
    1
  • Djan Unchained
    requestAnimationFrame 也是在主线程上执行吗?如果当前任务执行时间过久,也会导致 requestAnimationFrame 被延后执行吗?

    作者回复: 是的,raf的回调函数也是在主线程上执行的,如果其中的一个回调函数执行过久,会影响到其他的任务的

    2019-10-25
    1
  • Debugger
    请问老师,系统每隔16ms去刷新页面,那刷新页面这个操作是不是也是消息队列中的一个任务
    2019-10-12
    1
  • 阿桐
    老师,假设在执行延迟队列中到期的任务时产生了新的到期任务如 setTimeout(foo, 0),新的到期任务是在本次循环中执行还是推迟到下个循环中执行呢?

    “如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔为 4 毫秒”,基于此,我猜测新产生的到期任务不超过5个的话,会在本次循环中执行
    2019-09-25
    1
  • Twilight
    requestAnimationFrame与浏览器刷新同步,而setTimeout就算设置了16.7延迟,也可能会因为当前任务的执行时间过长而延迟
    2019-09-10
    1
  • -_-_aaa
    '32bit 最大只能存放的数字是 2147483647 毫秒',最大能存放的数字不是2^32-1吗?4294967295,为什么是 2147483647 毫秒

    作者回复: 因为要一位表示正负数啊

    2019-12-06
  • 玉皇大亮
    setTimeout总结:
    浏览器怎么实现setTimeout?
    在消息循环系统中,除了有任务处理队列,还有延时处理队列,浏览器将待延时的任务放在延时处理队列中
    setTimeout使用注意事项:
    1.如果当前执行任务时间过久,会影响延时任务的执行
    2.如果setTimeout存在嵌套调用,系统回存在4ms的延时
    3.未激活的页面下用的延时最小为1000ms,出于加载性能损耗以及降低耗电量
    4.chrome、safari、firefox中最大延时为2147483647ms,如果超过这个阈值,那么溢出,延时为0,立即执行
    5.setTimeout中的this不符合直觉,处理需要用箭头函数或者bind来强制改变this指向
    2019-12-05
  • 海之蓝心
    老师,如果setInterval是怎么执行,是在延时队列还是微任务

    作者回复: 和setTimeout一样的

    2019-12-04
    1
  • 纪年
    如果 setTimeout 设置的延迟值大于 2147483647 毫秒时就会溢出,这导致定时器会被立即执行。
    问题:这里的立即执行其实是不是相当于setTimeout(fun, 0)的意思?

    作者回复: 是的,说立即执行的确有点不准确哈,这个我来优化下说法!

    2019-11-23
  • follaw
    系统如何筛选出到期的任务,如果有10000个呢,是循环一万次?这个系统内部怎么处理的呢?

    作者回复: 每次执行完一个任务后,都会计算下是否有定时器的任务到期

    2019-11-14
    1
  • 天河
    “这里我们要重点关注它的执行时机,在上段代码中,处理完消息队列中的一个任务之后,就开始执行 ProcessDelayTask 函数。ProcessDelayTask 函数会根据发起时间和延迟时间计算出到期的任务,然后依次执行这些到期的任务。等到期的任务执行完成之后,再继续下一个循环过程。”这里的意思是每执行一个宏任务就去检查异步队列,如果有 setTimeout 任务到期就执行。这里确定是执行吗?而不是将它们加入消息队列?
    2019-11-06
收起评论
31
返回
顶部