FE.ES-理解Event Loop
JavaScript引擎又称为JavaScript解释器,是JavaScript解释为机器码的工具,分别运行在浏览器和Node中。而根据上下文的不同,Event loop也有不同的实现:其中Node使用了libuv库来实现Event loop; 而在浏览器中,html规范定义了Event loop,具体的实现则交给不同的厂商去完成。
浏览器中的Event Loops
根据2017年新版的HTML规范HTML Standard,浏览器包含2类事件循环:browsing contexts 和 web workers。
browsing contexts中有一个或多个Task Queue,即MacroTask Queue,仅有一个Job Queue,即MicroTask Queue。
macrotask queue(宏任务,不妨称为
A
)- setTimeout
- setInterval
- setImmediate(node独有)
- requestAnimationFrame
- I/O
- UI rendering
microtask queue(微任务,不妨称为
I
)- process.nextTick(node独有)
- Promises
- Object.observe(废弃)
- MutationObserver
这两个任务队列执行顺序:
- 取1个
A
中的task,执行之。 - 把所有
I
顺序执行完,再取A
中的下一个任务。
为什么promise.then的回调比setTimeout先执行
代码开始执行时,所有这些代码在A
中,形成一个执行栈(execution context stack),取出来执行之。
遇到setTimeout,则加到A
中,遇到promise.then,则加到I
中。
等整个执行栈执行完,取I
中的任务。
(function test() {setTimeout(function() {console.log(4)}, 0);new Promise(function executor(resolve) {console.log(1);for( var i=0 ; i<10000 ; i++ ) {i == 9999 && resolve();}console.log(2);}).then(function() {console.log(5);});console.log(3);
})()
// 1
// 2
// 3
// 5
// 4
//浏览器渲染步骤:Structure(构建 DOM) ->Layout(排版)->Paint(绘制)
//新的异步任务将在下一次被执行,因此就不会存在阻塞。
button.addEventListener('click', () => {setTimeout(fn, 0)
})
V8源码
https://github.com/v8/v8/blob...
https://github.com/v8/v8/blob...
NodeJS中的Event Loop
而在Node.js中,microtask会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。
node新加了一个微任务process.nextTick
和一个宏任务setImmediate
.
process.nextTick
在当前"执行栈"的尾部(下一次Event Loop之前)触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。
process.nextTick(function A() {console.log(1);process.nextTick(function B(){console.log(2);});
});setTimeout(function timeout() {console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED
setImmediate
setImmediate
方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。
setImmediate(function A() {console.log(1);setImmediate(function B(){console.log(2);});
});setTimeout(function timeout() {console.log('TIMEOUT FIRED');
}, 0);
//不确定
递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()
process.nextTick(function foo() {process.nextTick(foo);
});
//FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory
process.nextTick也会放入microtask quque,为什么优先级比promise.then高呢
在Node中,_tickCallback在每一次执行完TaskQueue中的一个任务后被调用,而这个_tickCallback中实质上干了两件事:
- nextTickQueue中所有任务执行掉(长度最大1e4,Node版本v6.9.1)
- 第一步执行完后执行_runMicrotasks函数,执行microtask中的部分(promise.then注册的回调)所以很明显process.nextTick > promise.then”
node.js的特点是事件驱动,非阻塞单线程。当应用程序需要I/O操作的时候,线程并不会阻塞,而是把I/O操作交给底层库(LIBUV)。此时node线程会去处理其他任务,当底层库处理完I/O操作后,会将主动权交还给Node线程,所以Event Loop的用处是调度线程,例如:当底层库处理I/O操作后调度Node线程处理后续工作,所以虽然node是单线程,但是底层库处理操作依然是多线程。
根据Node.js官方介绍,每次事件循环都包含了6个阶段,对应到 libuv 源码中的实现,如下图所示
timers :这个阶段执行timer(setTimeout、setInterval)的回调
I/O callbacks:执行一些系统调用错误,比如网络通信的错误回调
idle, prepare :仅node内部使用
poll :获取新的I/O事件, 适当的条件下node将阻塞在这里
check :执行 setImmediate() 的回调
close callbacks :执行 socket 的 close 事件回调
timers阶段
timers 是事件循环的第一个阶段,Node 会去检查有无已过期的timer,如果有则把它的回调压入timer的任务队列中等待执行,事实上,Node 并不能保证timer在预设时间到了就会立即执行,因为Node对timer的过期检查不一定靠谱,它会受机器上其它运行程序影响,或者那个时间点主线程不空闲。但是把它们放到一个I/O回调里面,就一定是 setImmediate() 先执行,因为poll阶段后面就是check阶段。
I/O callbacks 阶段
这个阶段主要执行一些系统操作带来的回调函数,如 TCP 错误,如果 TCP 尝试链接时出现 ECONNREFUSED 错误 ,一些 *nix 会把这个错误报告给 Node.js。而这个错误报告会先进入队列中,然后在 I/O callbacks 阶段执行。
poll 阶段
poll 阶段主要有2个功能:
- 处理 poll 队列的事件
- 当有已超时的 timer,执行它的回调函数
even loop将同步执行poll队列里的回调,直到队列为空或执行的回调达到系统上限(上限具体多少未详),接下来even loop会去检查有无预设的setImmediate(),分两种情况:
- 若有预设的setImmediate(), event loop将结束poll阶段进入check阶段,并执行check阶段的任务队列
- 若没有预设的setImmediate(),event loop将阻塞在该阶段等待
注意一个细节,没有setImmediate()会导致event loop阻塞在poll阶段,这样之前设置的timer岂不是执行不了了?所以咧,在poll阶段event loop会有一个检查机制,检查timer队列是否为空,如果timer队列非空,event loop就开始下一轮事件循环,即重新进入到timer阶段。
check 阶段
setImmediate()的回调会被加入check队列中, 从event loop的阶段图可以知道,check阶段的执行顺序在poll阶段之后。
close 阶段
突然结束的事件的回调函数会在这里触发,如果 socket.destroy(),那么 close 会被触发在这个阶段,也有可能通过 process.nextTick() 来触发。
示例
setTimeout(()=>{console.log('timer1')Promise.resolve().then(function() {console.log('promise1')})
}, 0)setTimeout(()=>{console.log('timer2')Promise.resolve().then(function() {console.log('promise2')})
}, 0)
/*浏览器中
timer1
promise1
timer2
promise2
*/
/*node中
timer1
timer2
promise1
promise2
*/
const fs = require('fs')fs.readFile('test.txt', () => {console.log('readFile')setTimeout(() => {console.log('timeout')}, 0)setImmediate(() => {console.log('immediate')})
})
/*
readFile
immediate
timeout
*/
更多示例
libuv源码
https://github.com/libuv/libu...
其他
requestAnimationFrame
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()
客户端可能实现了一个包含鼠标键盘事件的任务队列,还有其他的任务队列,而给鼠标键盘事件的任务队列更高优先级,例如75%的可能性执行它。这样就能保证流畅的交互性,而且别的任务也能执行到了。但是,同一个任务队列中的任务必须按先进先出的顺序执行。
用户点击与button.click()的区别:
用户点击:依次执行listener。浏览器并不实现知道有几个 listener,因此它发现一个执行一个,执行完了再看后面还有没有。
click:同步执行listener。 click方法会先采集有哪些 listener,再依次触发。
示例详情
参考资料
Promise的队列与setTimeout的队列有何关联?
浏览器的 Event Loop
Event Loops
深入理解js事件循环机制(Node.js篇)
JavaScript 运行机制详解:再谈Event Loop
Node.js 事件循环,定时器和 process.nextTick()
FE.ES-理解Event Loop相关推荐
- JavaScript中事件循环的理解 Event Loop
为了解决单线程运行阻塞问题,JavaScript用到了计算机系统的一种运行机制,这种机制就叫做事件循环(Event Loop) 事件循环(Event Loop) 在JavaScript中,所有的任务都 ...
- 【js进阶】全面理解Event Loop这一篇就够了
文章目录 一.前言 二.Event Loop知识铺垫 1.微任务(MircoTask) 2.宏任务(MacroTask/Task) 3.javascript runtime 三.浏览器环境的Event ...
- 深入理解 Event Loop
众所周知,JavaScript(以下简称 JS) 是单线程语言,在 html5 中增加了 web workers,web workers 是新开了线程执行的,那么 JS 还是单线程的吗?当然是,为什么 ...
- 跟着 Event loop 规范理解浏览器中的异步机制
原文发自我的 GitHub blog,欢迎关注 前言 我们都知道 JavaScript 是一门单线程语言,这意味着同一事件只能执行一个任务,结束了才能去执行下一个.如果前面的任务没有执行完,后面的任务 ...
- 理解node.js中的 Event Loop
node中的 "event loop" 正是node能处理高并发的核心所在.也正是因为它,node虽然在本质上是个单线程,却能让大量的操作处于后台运行.这篇文章将详细说明 even ...
- JavaScript 运行机制详解:Event Loop
转自: http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单 ...
- 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop
PS: 我先旁观下大师们的讨论,得多看书了~ 别人说的:"看了一下不觉得评注对到哪里去,只有吹毛求疵之感. 比如同步异步介绍,本来就无大错:比如node图里面的OS operation,推敲 ...
- JavaScript 运行机制详解:再谈Event Loop
原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...
- Js Event Loop
Js Event Loop 本文主要介绍 Node 中的事件循环 参考 官网 (什么是 Event Loop? - 阮一峰的网络日志 (ruanyifeng.com)) What is the Eve ...
最新文章
- python使用方法视频-Python读取视频的两种方法(imageio和cv2)
- 推荐 15 款常用开发工具
- 123_Power PivotPower BI DAX函数说明速查
- java获取请求的url地址
- 大数据有哪些特点和作用
- OpenStack云计算快速入门之三:OpenStack镜像管理
- 近年来最流行网络词汇及论坛用语
- ActiveReports 9 新功能:创新的报表分层设计理念
- ae批量修改字体_AE脚本pt_TextEdit v2.5批量修改文字图层样式字体大小属性编辑工具...
- 51单片机实训(一)————Keil 基本操作
- 【转】CT (电子计算机断层扫描)
- 织梦模板建站必须学会的基本代码
- 如何修改PDF内容,PDF怎么旋转页面
- “贵州与您相约”英文网站8月18日正式上线;大华股份助力长兴打造景区数字化管理样板 | 全球旅报...
- SCOI2014 方伯伯的玉米田 题解
- Failure to find xxx in http://maven.aliyun.com/nexus/content/groups/public
- 新版Chrome自动禁用第三方插件的解决办法[转]
- Seata源码走读分析
- 怎么退出用户登录linux,linux如何退出用户
- sap 标准委外和工序委外_SAP PP 工序委外详解
热门文章
- Django之form组件加cookie,session
- Android源码分析(一)-----如何快速掌握Android编译文件
- bzoj 1015 [JSOI2008]星球大战starwar
- 桥接模式和php实现
- iOS-推送通知详解
- SharePoint 2013 Ajax 造成页面无法编辑
- 访问被拒绝:“Interop.jmail”
- stl之set集合容器应用基础
- 简要说明php数组的类型,php数组的概述及分类与声明代码演示
- python变量命名规则思维导图_python基础知识点思维导图