16 | WebAPI:setTimeout是如何实现的?
16 | WebAPI:setTimeout是如何实现的?
讲述:李兵
时长10:59大小10.07M
浏览器怎么实现 setTimeout
使用 setTimeout 的一些注意事项
1. 如果当前任务执行时间过久,会影响定时器任务的执行
2. 如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔为 4 毫秒
3. 未激活的页面,setTimeout 执行最小间隔是 1000 毫秒
4. 延时执行时间有最大值
5. 使用 setTimeout 设置的回调函数中的 this 不符合直觉
总结
思考时间
赞 45
提建议
精选留言(82)
- pyhhou2019-09-10使用 requestAnimationFrame 不需要设置具体的时间,由系统来决定回调函数的执行时间,requestAnimationFrame 里面的回调函数是在页面刷新之前执行,它跟着屏幕的刷新频率走,保证每个刷新间隔只执行一次,内如果页面未激活的话,requestAnimationFrame 也会停止渲染,这样既可以保证页面的流畅性,又能节省主线程执行函数的开销共 9 条评论109
- Angus2019-09-10我没有太理解这个异步延迟队列,既然是队列,但好像完全不符合先进先出的特点。在每次执行完任务队列中的一个任务之后都会去执行那些已经到期的延迟任务,这些延迟的任务具体是如何取出的呢。
作者回复: 我文章说是队列,为了和消息队列统一起来,不然表述起来有点拗口。 其实是一个hashmap结构,等到执行这个结构的时候,会计算hashmap中的每个任务是否到期了,到期了就去执行,直到所有到期的任务都执行结束,才会进入下一轮循环!
共 5 条评论97 - mfist2019-09-11requestAnimationFrame 提供一个原生的API去执行动画的效果,它会在一帧(一般是16ms)间隔内根据选择浏览器情况去执行相关动作。 setTimeout是在特定的时间间隔去执行任务,不到时间间隔不会去执行,这样浏览器就没有办法去自动优化。 今日得到 浏览器的页面是通过消息队列和事件循环系统来驱动的。settimeout的函数会被加入到延迟消息队列中, 等到执行完Task任务之后就会执行延迟队列中的任务。然后分析几种场景下面的setimeout的执行方式。 1. 如果执行一个很耗时的任务,会影响延迟消息队列中任务的执行 2. 存在嵌套带调用时候,系统会设置最短时间间隔为4s(超过5层) 3. 未激活的页面,setTimeout最小时间间隔为1000ms 4. 延时执行时间的最大值2147483647,溢出会导致定时器立即执行 5. setTimeout设置回调函数this会是回调时候对应的this对象,可以使用箭头函数解决展开
作者回复: 回答的很棒,raf是按照系统刷新的节奏调用的!
共 3 条评论52 - moss2019-09-18这一节学习到了不少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是用户调用的,重新布局是渲染引擎自动安排的任务,必然要放到消息队列中! 滚动一般默认都是在合成线程里面完成的,这种都没用到主线程
共 8 条评论18 - l1shu2019-10-22为什么有些文章说渲染进程中有一个定时器线程用来计时的 到时间后会把回调函数塞到消息队列 而没有提到延迟队列这个说法 求老师解答共 4 条评论17
- Helios2019-09-11请问老师不是说settimeout属于宏任务不,不应该属于在上节课讲的消息队列中么 怎么这次有跑到延时队列中了呢,这两个队列有什么关系呢,延时队列也分宏任务和微任务?共 4 条评论16
- 吴海燕2020-05-22老师有空的时候能否画一个包含延时队列和微任务,宏任务,消息队列关系运行图13
- 淡2019-09-17老师,你好。 请问微任务的执行是在延迟队列任务执行之前吗?
作者回复: 没有之前之后啊,延时队列里面是宏任务,普通的消息队列里面也是宏任务,微任务是在宏任务快要执行结束之前执行的!
共 6 条评论10 - Zzzrd2019-12-19看完还是很迷惑: 1. setTimeout是宏任务,宏任务应该放在消息队列中,文中说是放在延迟队列中,为什么?延迟队列和消息队列的区别是什么? 2. 延迟队列的任务是在当前宏任务执行完之后执行,微任务队列是在当前宏任务将要结束时执行对吗?
作者回复: 延迟队列也是宏任务,实际上blink维护了很多不同优先级的队列,这些队列里面都是宏任务 微任务是在宏任务执行过程中的某个时间点执行的,通常是在宏任务快要结束的时候执行
共 4 条评论9 - Djan Unchained2019-10-25requestAnimationFrame 也是在主线程上执行吗?如果当前任务执行时间过久,也会导致 requestAnimationFrame 被延后执行吗?
作者回复: 是的,raf的回调函数也是在主线程上执行的,如果其中的一个回调函数执行过久,会影响到其他的任务的
9 - Wlt2019-10-28老师,您好,延迟队列和消息队列是什么关系,怎么配合工作的?
作者回复: 延迟消息队列主要是放一些定时执行的任务,如JavaScript设置定时器的回调,还有浏览器内部的一些定时回调任务! 这类任务需要等到指定时间间隔之后才会被执行! 而正常的消息队列中的任务只会按照顺序执行,执行完上个任务接着执行下个任务,不需要关系时间间隔!
8 - 李懂2019-09-101.执行延迟队列的任务,是一次循环只取出一个,还是检查只要时间到了,就执行? 2.微任务是在宏任务里的,是执行完一个宏任务,就去执行宏任务里面的微任务?
作者回复: 比如有五个定时的任务到期了,那么会分别把这个五个定时器的任务执行掉,再开始下次循环过程! chromium中,当执行一个宏任务时,才会创建微任务队列,等遇到checkpoint时就会执行微任务!
共 3 条评论8 - 4!!2020-01-31传入requestAnimationFrame的回调并不会添加到消息队列或延迟队列中,传入requestAnimationFrame的回调会在页面下次重绘之前被调用,可以保证动画更实时准确。与setTimeout相比还有几个优点:1.当页面不可见或未被激活时,requestAnimationFrame的回调不会被调用;2.requestAnimationFrame的循环调用会有个自动节流处理,使得动画足够流畅,而函数不被过于频繁调用。共 2 条评论6
- 穿秋裤的男孩2020-04-16评论好多说延迟队列得,其实就是一个定时器线程吧,定时器线程负责计时,到点了就把回掉push到消息队列中。共 4 条评论5
- Cris2020-01-03老师,您这里未激活的页面是什么意思?
作者回复: 就是后台页面,比如你在浏览器中打开了多个标签,除了你当前操作的页面,其他的标签页都是后台页面
4 - 晓东2019-12-18老师,对processDelayTask这块有个疑惑。这里会把所有的到期任务都执行完才会开始下一个while循环吗?
作者回复: 是的
3 - 纪年2019-11-23如果 setTimeout 设置的延迟值大于 2147483647 毫秒时就会溢出,这导致定时器会被立即执行。 问题:这里的立即执行其实是不是相当于setTimeout(fun, 0)的意思?
作者回复: 是的,说立即执行的确有点不准确哈,这个我来优化下说法!
3 - follaw2019-11-14系统如何筛选出到期的任务,如果有10000个呢,是循环一万次?这个系统内部怎么处理的呢?
作者回复: 每次执行完一个任务后,都会计算下是否有定时器的任务到期
共 2 条评论3 - 以武会友-刘华强2020-04-14怎么优化setTimeout 时间值不准的问题呢?共 1 条评论2
- -_-|||2019-12-06'32bit 最大只能存放的数字是 2147483647 毫秒',最大能存放的数字不是2^32-1吗?4294967295,为什么是 2147483647 毫秒
作者回复: 因为要一位表示正负数啊
2