JavaScript:event loop详解
之前已经有两篇随笔提到了event loop,一篇是事件机制,一篇是tasks和microtasks,但是里面的event loop都是文字描述,很难说细,逻辑也只是简单的提了一遍。其实之前也是通过阮一峰老师的一篇网络日志:再谈event loop,然后写了点自己的想法。但是总感觉里面一些细节没有提到,像微任务队列这种。后来通过查看了一些国外的文档,尤其是一些谷歌Chrome开发人员的技术文档,并且结合了whatwg的一些HTML标准,这才有了点较全面的认识,这里把它记录下来。
这里先来看一段代码,这是HTML规范里提到的:
eventLoop = {
taskQueues: {
events: [], // UI events from native GUI framework
parser: [], // HTML parser
callbacks: [], // setTimeout, requestIdleTask
resources: [], // image loading
domManipulation: []
},
microtaskQueue: [
],
nextTask: function() {
// Spec says:
// "Select the oldest task on one of the event loop's task queues"
// Which gives browser implementers lots of freedom
// Queues can have different priorities, etc.
for (let q of taskQueues)
if (q.length > 0)
return q.shift();
return null;
},
executeMicrotasks: function() {
if (scriptExecuting)
return;
let microtasks = this.microtaskQueue;
this.microtaskQueue = [];
for (let t of microtasks)
t.execute();
},
needsRendering: function() {
return vSyncTime() && (needsDomRerender() || hasEventLoopEventsToDispatch());
},
render: function() {
dispatchPendingUIEvents();
resizeSteps();
scrollSteps();
mediaQuerySteps();
cssAnimationSteps();
fullscreenRenderingSteps();
animationFrameCallbackSteps();
intersectionObserverSteps();
while (resizeObserverSteps()) {
updateStyle();
updateLayout();
}
paint();
}
}
while(true) {
task = eventLoop.nextTask();
if (task) {
task.execute();
}
eventLoop.executeMicrotasks();
if (eventLoop.needsRendering())
eventLoop.render();
}
事件循环并没有多说关于什么时候dispatch event:
1,每一个queue(队列)中的事件都是按顺序执行;
2,事件可以直接dispatch,绕过task queues(任务队列)
2,微任务是在一个task执行完成后立即执行;
3,渲染部分循环是在vSync上执行,并且按以下顺序传递事件:
1️⃣分派待处理的UI事件,2️⃣resize事件,3️⃣scroll滚动事件,4️⃣mediaquery监听者(css @media),5️⃣CSSAnimation事件,6️⃣Observers,7️⃣requestAnimationFrame
下面来详细说一下
UI events有两类:
1,Discrete,俗称离散事件,就是那些不连续的,比如(mousedown,mouseup,touchstart,touchend)等
2,Continuous,俗称连续事件,就是连续的,比如(mousemove,mousewheel,touchmove,wheel)等
两种事件的注意点不同:
1,连续事件:一个UI event task queue中,相匹配的连续事件(比如持续更新position属性,或者持续改变大小),可能会合并,不管那些合并的事件是否被dispatch了,因为有的还没有被dispatch,或者排在了队列的后面。
2,离散事件:如果从硬件接收到了离散事件,就必须尽快dispatch,如果此时队列中有连续事件,就必须立即运行所有的连续事件,以防止离散事件的延迟。也就是说,触发离散事件的时候,连续事件必定已经全部dispatch完毕。
不同的浏览器对事件循环的顺序是不同的,我还是习惯以谷歌为准,因为谷歌浏览器是最接近规范的
下面列举一些谷歌dispatch event是通过哪些方法来dispatch的:
1,DOMWindowEventQueue
由计时器触发
示例事件:window.storage,window.hashchange,document.selectionchange
2,ScriptedAnimationController
由Frame调用的BeginMainFrame函数触发,同时还管理requestAnimationFrame请求
示例事件:Animation.finish,Animation.cancel,CSSAnimation.animationstart,CSSAnimation.animationiteration(CSSAnimation)
3,Custom dispatch
触发器不同:OS events操作系统事件,timers定时器,文档/元素生命周期事件
Custom dispatch event 不通过队列,他们直接被触发(这里我猜想可能就是‘任务队列’的隐藏概念,他们不会排在普通队列中,而是单独去了另一个特殊的队列执行,这里就是‘任务队列’)
4,Microtask队列
微任务通常由EndOfTaskRunner.didProcessTask()触发,任务由TaskQueueManager运行,每当task完成时,微任务队列就会执行
示例事件:image.onerror,image.onload
微任务也包括Promise callbacks,这里注意,Promise的回调才是真正的微任务,之前说的可能不严谨,执行promise的时候本身还是正常task
5,主线程事件队列
连续事件会被合并处理。
关于Timer有几个方法:
requestIdleCallback
这个方法只有浏览器空闲的时候才会有内部的timer触发
requestAnimationFrame
由ScriptedAnimationController触发,这个方法挺重要的,主要是用来解决setTimeout和setInterval无法完成的动画效果。
Timers:setTimeout,setInterval
由运行在TaskQueue primitive上的WebTaskRunner触发的。
Observers
观察者分为两种,MutationObserver和IntersectionObserver。
MutationObserver:属于微任务,主要是负责监听DOM节点内的变化,前一篇随笔里有提到;
IntersectionObserver:属于轮询,可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。具体用法不多介绍,不过它可以用来实现懒加载、无限滚动,监听元素是否在视窗中,或者已经出现了多少内容。
Promises
执行完成后,回调会放到微任务队列中去。
以上就是event loop的全部相关内容。
========= 这条是分割线 ========
2018年2月14日情人节
其实结合上一篇‘再谈tasks和microtasks’
上面提到微任务是在一个task执行完成之后才会执行,而前一篇里提到当JS栈清空时微任务才会执行,但是存在一个task中JS栈会清空的情况(例如那个click冒泡事件),而一个task并未执行完成。这里面就会有一个矛盾点,微任务并没有在task执行完成后执行,而是中途就执行了。
针对此问题,我私信了Google Chrome的developer,Jake Archibald,感谢他不吝指教:

那其实很明确了,微任务是否执行完全取决于JS stack是否为空,而task是否执行完成,这并不重要。一次任务中可能会有多个事件监听器,每次执行完一次事件监听,JS stack就会空一次,这时候就会执行微任务了。

===================================
2018.02.23 下午补充:
nodejs里也有事件循环的改变,它是nodejs自身的执行模型:
它是一个类似于while(true)的循环,每次循环都会去询问是否有事件需要处理,如果有,取出一个事件,查看该事件是否有对应的回调函数,如果有,执行函数然后进入下一个循环。如果不再有事件处理,则退出进程。
如何得知是否有事件需要处理呢?问谁?
观察者。每一个事件对应一个观察者,有文件I/O观察者,网络I/O观察者等等,观察者将事件进行了分类。
事件循环是一个典型的生产者/消费者模型。异步I/O、网络请求是事件的生产者,为Node提供不同类型的事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。
在Windows下,这个循环基于IOCP创建,而在*nix下则基于多线程创建。
end
JavaScript:event loop详解的更多相关文章
- Event Loop详解
1.进程,单线程与多线 进程: 运行的程序就是一个进程,比如你正在运行的浏览器,它会有一个进程. 线程: 程序中独立运行的代码段. 一个进程由单个或多个线程组成,线程是负责执行代码的. 2.单线程与多 ...
- Event loop详解(包含Node端)
Event loop事件循环,是一个执行模型.不同的浏览器以及Nodejs里的具体实现是不一样的. 一,浏览器端: HTML5规范里有明确定义,简单的说: 1,JS是单线程的,执行的时候在一条主栈上: ...
- JavaScript——event事件详解
1.事件对象 Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态. 什么时候会产生Event 对象呢? 例如: 当用户单击某个元素的时候,我们给这个元 ...
- (转)javascript中event对象详解
原文:http://jiajiale.iteye.com/blog/195906 javascript中event对象详解 博客分类: javaScript JavaScriptCS ...
- [转]JavaScript异步机制详解
原文: https://www.jianshu.com/p/4ea4ee713ead --------------------------------------------------------- ...
- JavaScript Event Loop和微任务、宏任务
为什么JavaScript是单线程? JavaScript的一大特点就是单线程, 同一时间只能做一件事情,主要和它的用途有关, JavaScript主要是控制和用户的交互以及操作DOM.注定它是单线程 ...
- 从mixin到new和prototype:Javascript原型机制详解
从mixin到new和prototype:Javascript原型机制详解 这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...
- JavaScript严格模式详解
转载自阮一峰的博客 Javascript 严格模式详解 作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...
- JS中的event 对象详解
JS中的event 对象详解 JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...
随机推荐
- 什么是BOM?,什么是DOM? BOM跟DOM之间的关系
什么是BOM? BOM是browser object model的缩写,简称浏览器对象模型.是用来获取或设置浏览器的属性.行为,例如:新建窗口.获取屏幕分辨率.浏览器版本号等. 比如 alert(); ...
- Servlet的几个关键知识点
1.ServletConfig ServletConfig是Servlet的配置文件.对应于web.xml中的<servlet></servlet>标签.ServletConf ...
- cookie与session的区别是什么
cookie与session的区别有:cookie以文本格式存储在浏览器上,存储量有限:而会话存储在服务端,可以无限量存储多个变量并且比cookie更安全 在php中可以指定站点的访问者信息存储在se ...
- JVM内存区域详解(Eden Space、Survivor Space、Old Gen、Code Cache和Perm Gen)
JVM区域总体分两类,heap区和非heap区.heap区又分为: Eden Space(伊甸园). Survivor Space(幸存者区). Old Gen(老年代). 非heap区又分: Cod ...
- python变量、注释、程序交互、格式化输入、基本运算符
变量 ...
- Python代码教你批量将PDF转为Word
很多时候在学习时发现许多文档都是PDF格式,PDF格式却不利于学习使用,因此需要将PDF转换为Word文件,但或许你从网上下载了很多软件,但只能转换前五页(如WPS等),要不就是需要收费,那有没有免费 ...
- Cuda9.2发布
经过一次跳票,没有很多人注意(才怪)的cuda9.2终于发布了,官方介绍如下: CUDA 9.2 includes updates to libraries, a new library for ac ...
- docker安装portainer
安装好docker之后,可以使用portainer对容器进到管理 docker安装portainer命令 #这一步可以省略,直接运行可以下一条docker pull portainer #因为dock ...
- xadmin设置
1.xadmin配置 INSTALLED_APPS = ( ... 'xadmin' , 'crispy_forms' , 'reversion' , ... ) 2.注册类 import xadmi ...
- 在IDEA中停止和关闭SonarLint自动检查,手动运行SonarLint检查代码
关闭SonarLint自动检查代码 有时敲一行代码SonarLint插件就会自动检查,让人感觉很不舒服,还会使电脑卡顿: 依次点击:File -> Settings 或直接Ctrl+Alt+S ...