详解JS浏览器事件循环机制


Posted in Javascript onMarch 27, 2019

先来明白些概念性内容。

进程、线程

进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。

线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。

浏览器内核

浏览器是多进程的,浏览器每一个 tab 标签都代表一个独立的进程(也不一定,因为多个空白 tab 标签会合并成一个进程),浏览器内核(浏览器渲染进程)属于浏览器多进程中的一种。

浏览器内核有多种线程在工作。

GUI 渲染线程:

  1. 负责渲染页面,解析 HTML,CSS 构成 DOM 树等,当页面重绘或者由于某种操作引起回流都会调起该线程。
  2. 和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 任务队列中,等待 JS 引擎线程空闲的时候继续执行。

JS 引擎线程:

  1. 单线程工作,负责解析运行 JavaScript 脚本。
  2. 和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。

事件触发线程:

当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待 JS 引擎处理。

定时器触发线程:

  1. 浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不准确。
  2. 开启定时器触发线程来计时并触发计时,计时完成后会被添加到任务队列中,等待 JS 引擎处理。

http 请求线程:

  1. http 请求的时候会开启一条请求线程。
  2. 请求完成有结果了之后,将请求的回调函数添加到任务队列中,等待 JS 引擎处理。

 详解JS浏览器事件循环机制

JavaScript 引擎是单线程

JavaScript 引擎是单线程,也就是说每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后才会往下执行下一个任务。

HTML5 中提出了 Web-Worker API,主要是为了解决页面阻塞问题,但是并没有改变 JavaScript 是单线程的本质。了解 Web-Worker。

JavaScript 事件循环机制

JavaScript 事件循环机制分为浏览器和 Node 事件循环机制,两者的实现技术不一样,浏览器 Event Loop 是 HTML 中定义的规范,Node Event Loop 是由 libuv 库实现。这里主要讲的是浏览器部分。

Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

详解JS浏览器事件循环机制

详解JS浏览器事件循环机制

定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。

定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。

macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。

console.log(1);
setTimeout(function() {
  console.log(2);
})
var promise = new Promise(function(resolve, reject) {
  console.log(3);
  resolve();
})
promise.then(function() {
  console.log(4);
})
console.log(5);

示例中,setTimeout 和 Promise被称为任务源,来自不同的任务源注册的回调函数会被放入到不同的任务队列中。

有了宏任务和微任务的概念后,那 JS 的执行顺序是怎样的?是宏任务先还是微任务先?

第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。

  1. 上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行。
  2. 遇到了 setTimeout ,就会等到过了指定的时间后将回调函数放入到宏任务的任务队列中。
  3. 遇到 Promise,将 then 函数放入到微任务的任务队列中。
  4. 整个事件循环完成之后,会去检测微任务的任务队列中是否存在任务,存在就执行。
  5. 第一次的循环结果打印为: 1,3,5,4。
  6. 接着再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。
  7. 检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
  8. 最终的结果就是 1,3,5,4,2。

以上所述是小编给大家介绍的JS浏览器事件循环机制详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Dom 学习总结以及实例的使用介绍
Apr 24 Javascript
jQuery中noconflict函数的实现原理分解
Feb 03 Javascript
jQuery表单验证功能实例
Aug 28 Javascript
原生JavaScript实现动态省市县三级联动下拉框菜单实例代码
Feb 03 Javascript
BootStrap智能表单实战系列(四)表单布局介绍
Jun 13 Javascript
AngularJS基础 ng-submit 指令简单示例
Aug 03 Javascript
Bootstrap实现渐变顶部固定自适应导航栏
Aug 27 Javascript
vue.js 1.x与2.0中js实时监听input值的变化
Mar 15 Javascript
Angularjs 动态添加指令并绑定事件的方法
Apr 13 Javascript
移动端web滚动分页的实现方法
May 05 Javascript
JS处理一些简单计算题
Feb 24 Javascript
JavaScript 中 JSON.parse 函数 和 JSON.stringify 函数
Dec 05 Javascript
详解如何更好的使用module vuex
Mar 27 #Javascript
原生js实现获取form表单数据代码实例
Mar 27 #Javascript
JQueryDOM之样式操作
Mar 27 #jQuery
Element中的Cascader(级联列表)动态加载省\市\区数据的方法
Mar 27 #Javascript
Node.js 多线程完全指南总结
Mar 27 #Javascript
浅谈JS和jQuery的区别
Mar 27 #jQuery
Nginx设置为Node.js的前端服务器方法总结
Mar 27 #Javascript
You might like
模拟SQLSERVER的两个函数:dateadd(),datediff()
2006/10/09 PHP
php 友好URL的实现(吐血推荐)
2008/10/04 PHP
php使用ereg验证文件上传的方法
2014/12/16 PHP
jQuery 1.0.2
2006/10/11 Javascript
formStorage 基于jquery的一个插件(存储表单中元素的状态到本地)
2012/01/20 Javascript
javaScript中Math()函数注意事项
2015/06/18 Javascript
两款JS脚本判断手机浏览器类型跳转WAP手机网站
2015/10/16 Javascript
基于jQuery实现带动画效果超炫酷的弹出对话框(附源码下载)
2016/02/22 Javascript
浅谈jquery的map()和each()方法
2016/06/12 Javascript
JS实现屏蔽网页右键复制及ctrl+c复制的方法【2种方法】
2016/09/04 Javascript
node.js 抓取代理ip实例代码
2017/04/30 Javascript
基于 webpack2 实现的多入口项目脚手架详解
2017/06/26 Javascript
[01:10:03]OG vs EG 2018国际邀请赛淘汰赛BO3 第三场 8.23
2018/08/24 DOTA
17个Python小技巧分享
2015/01/23 Python
Python使用正则匹配实现抓图代码分享
2015/04/02 Python
分析Python编程时利用wxPython来支持多线程的方法
2015/04/07 Python
python使用PyCharm进行远程开发和调试
2017/11/02 Python
python docx 中文字体设置的操作方法
2018/05/08 Python
Django添加sitemap的方法示例
2018/08/06 Python
python+splinter自动刷新抢票功能
2018/09/25 Python
windows下搭建python scrapy爬虫框架步骤
2018/12/23 Python
Python静态类型检查新工具之pyright 使用指南
2019/04/26 Python
Python2比较当前图片跟图库哪个图片相似的方法示例
2019/09/28 Python
解决Python中导入自己写的类,被划红线,但不影响执行的问题
2020/07/13 Python
python利用faker库批量生成测试数据
2020/10/15 Python
澳洲健康食品网上商店:Aussie Health Products
2018/06/15 全球购物
MONNIER Frères英国官网:源自巴黎女士奢侈品配饰电商平台
2018/12/06 全球购物
Onzie官网:美国时尚瑜伽品牌
2019/08/21 全球购物
ECCO英国官网:丹麦鞋履品牌
2019/09/03 全球购物
Fenty Beauty官网:蕾哈娜创立的美妆品牌
2021/01/07 全球购物
艺术系应届生的自我评价
2013/10/19 职场文书
本科毕业生的求职信范文
2013/11/20 职场文书
学校运动会简讯
2015/07/20 职场文书
盘点2020年适合农村地区创业的项目
2019/10/16 职场文书
Python实现数据的序列化操作详解
2022/07/07 Python
centos环境下nginx高可用集群的搭建指南
2022/07/23 Servers