详解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 相关文章推荐
怎么让脚本或里面的函数在所有图片都载入完毕的时候执行
Oct 17 Javascript
javascript 特殊字符串
Feb 25 Javascript
js获取图片大小的函数代码
Sep 20 Javascript
JS构建页面的DOM节点结构的实现代码
Dec 09 Javascript
JS操作select下拉框动态变动(创建/删除/获取)
Jun 02 Javascript
ajax级联菜单实现方法实例分析
Nov 28 Javascript
深入浅析AngularJS中的一次性数据绑定 (bindonce)
May 11 Javascript
vue.js加载新的内容(实例代码)
Jun 01 Javascript
Vue2.0用户权限控制解决方案
Nov 29 Javascript
React全家桶环境搭建过程详解
May 18 Javascript
vue响应式更新机制及不使用框架实现简单的数据双向绑定问题
Jun 27 Javascript
vuex存值与取值的实例
Nov 06 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
discuz7 phpMysql操作类
2009/06/21 PHP
PHP采集相关教程之一 CURL函数库
2010/02/15 PHP
PHP+ajax 无刷新删除数据
2010/02/20 PHP
php中使用parse_url()对网址进行解析的实现代码(parse_url详解)
2012/01/03 PHP
thinkPHP3.1验证码的简单实现方法
2016/04/22 PHP
php实现图片上传时添加文字和图片水印技巧
2020/04/18 PHP
PHP中使用微秒计算脚本执行时间例子
2014/11/19 Javascript
jQuery+css3实现Ajax点击后动态删除功能的方法
2015/08/10 Javascript
AngularJs自定义服务之实现签名和加密
2016/08/02 Javascript
JavaScript 闭包详细介绍
2016/09/28 Javascript
Bootstrap Img 图片样式(推荐)
2016/12/13 Javascript
jQuery纵向导航菜单效果实现方法
2016/12/19 Javascript
原生js实现秒表计时器功能
2017/02/16 Javascript
jQuery Chosen通用初始化
2017/03/07 Javascript
AngularJS1.X学习笔记2-数据绑定详解
2017/04/01 Javascript
JavaScript获取tr td 的三种方式全面总结(推荐)
2017/08/15 Javascript
微信小程序onLaunch异步,首页onLoad先执行?
2018/09/20 Javascript
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
2018/09/29 Javascript
详解小程序用户登录状态检查与更新实例
2019/05/15 Javascript
使用vue实现一个电子签名组件的示例代码
2020/01/06 Javascript
[02:15]2014DOTA2国际邀请赛 专访LGD.lin小兔子是大腿
2014/07/14 DOTA
Python学习之asyncore模块用法实例教程
2014/09/29 Python
itchat和matplotlib的结合使用爬取微信信息的实例
2017/08/25 Python
python如何创建TCP服务端和客户端
2018/08/26 Python
Python实现查找最小的k个数示例【两种解法】
2019/01/08 Python
使用Python在Windows下获取USB PID&VID的方法
2019/07/02 Python
纯python进行矩阵的相乘运算的方法示例
2019/07/17 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
Pycharm插件(Grep Console)自定义规则输出颜色日志的方法
2020/05/27 Python
纯css3实现的动画按钮的实例教程
2014/11/17 HTML / CSS
考博专家推荐信模板
2013/12/02 职场文书
广告设计应届生求职信
2014/03/01 职场文书
处级干部考察材料
2014/12/24 职场文书
典型事迹材料范文
2014/12/29 职场文书
前台接待岗位职责
2015/02/03 职场文书
《总之就是很可爱》新作短篇动画《总之就是很可爱~制服~》将于2022年夏天播出
2022/04/07 日漫