js observer 添加_深入理解JS事件循环机制

关注“ 前端学苑 ” ,坚持每天进步一点点

94513c058fa8a6d0f536efe4f0791d72.png

「~JS事件循环机制 ~」

前段时间前端面试,经常会问这样的问题:

写出下面代码的运行结果:

console.log('1');setTimeout(function () {      console.log('2');}, 0);setTimeout(function () {      console.log('3');}, 2000);console.log('4');

看完这道题的我自信的写下了答案:1,4,2,3

与这段代码相关的知识点就是JavaScript事件循环机制,下面将从有关的基本概念出发,先了解了相关的概念,才能更好的理解事件循环的机制原理。

简短的概括:

1、线程与进程

2、浏览器是多进程的

3、浏览器内核(渲染进程)

4、JS是单线程语言

5、JS单线程又是如何实现异步

正文从这里开始~~~

请牢记2点:

1、JS是单线程语言。

2、JS的Event Loop(事件循环)是JS的执行机制。深入了解JS的执行,就等于深入了解JS里的event loop。

一、线程与进程

关于线程与进程的关系可以用下面的图进行说明:

ad91b15a0263e4b77196eef029b71f35.png

1) 进程好比图中的工厂,有单独的专属自己的工厂资源。

2) 线程好比图中的工人,多个工人在一个工厂中协作工作,工厂与工人是 1:n的关系。

3) 多个工厂之间独立存在。

官方的说法

1) 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)

2) 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

二、浏览器是多进程的

1) 浏览器是多进程的

2) 浏览器之所以能够运行,是因为系统给它的进程分配了资源(cpu、内存)

3) 简单点理解,每打开一个Tab页,就相当于创建了一个独立的浏览器进程

a174e2ff83a97f1d7add92bfafff59c3.png

图中打开了 Chrome浏览器的多个标签页,然后可以在 Chrome的任务管理器中看到有多个进程(分别是每一个Tab页面有一个独立的进程,以及一个主进程)。

三、浏览器内核(渲染进程)

对于前端操作来说,最终要的是渲染进程。

可以这样理解,页面的渲染,JS的执行,事件的循环,都在这个进程内进行。

那么接下来看看它都包含了哪些线程(列举一些主要常驻线程)

GUI渲染线程

1) 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。

2) 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。

3) 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

JS引擎线程 

也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)

1) JS引擎线程负责解析Javascript脚本,运行代码。

2) JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序。

3) 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

事件触发线程

1) 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)。

2) 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中。

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

4) 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)。

定时触发器线程

1) 传说中的setInterval与setTimeout所在线程。

2) 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)。

3) 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)。

注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

异步http请求线程

1) 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求

2) 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

看到这里,如果觉得累了,可以先休息下,这些概念需要被消化,毕竟后续将提到的事件循环机制就是基于事件触发线程的。

四、为什么JS是单线程

JS最初被设计用在浏览器中,那么想象一下,如果浏览器中的JS是多线程的。

场景描述:

那么现在有2个进程,process1 process2,由于是多进程的JS,所以他们对同一个dom,同时进行操作。process1 删除了该dom,而process2 编辑了该dom,同时下达2个矛盾的命令,浏览器究竟该如何执行呢?

这样想,JS为什么被设计成单线程应该就容易理解了吧。

五、为什么JS需要异步

场景描述:

如果JS中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验

所以,JS中存在异步执行。

六、JS单线程又是如何实现异步

既然JS是单线程的,只能在一条线程上执行,又是如何实现的异步呢?

是通过的事件循环(event loop),理解了event loop机制,就理解了JS的执行机制。

1e327a0afcd6993ed965dbcda109cc3c.png

导图要表达的内容用文字来表述的话:

1) 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table(事件列表)并注册函数。

2) 当指定的事情完成时,Event Table会将这个函数移入Event Queue(事件队列)。

3) 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

4) 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

事实上,按照异步和同步的划分方式,并不准确。

而准确的划分方式是:

1、微任务 (microtask):优先级高,并且可以插队,不是先定义先执行。包括:promise 中的 then,observer,MutationObserver,setImmediate。

2、宏任务 (macrotask):优先级低,先定义的先执行。包括:ajax,setTimeout,setInterval,事件绑定,postMessage,MessageChannel(用于消息通讯)。

5698eb25d62ff59a00f9c5a84e218d7f.png

* 举个栗子 ?

Promise.resolve().then(()=>{  console.log('Promise1')  setTimeout(()=>{    console.log('setTimeout2')  },0)})setTimeout(()=>{  console.log('setTimeout1')  Promise.resolve().then(()=>{    console.log('Promise2')  })},0)

最后输出结果是 Promise1,setTimeout1,Promise2,setTimeout2

根据事件循环机制,重新梳理一下流程:

1) 执行一个宏任务(首次执行的主代码块或者任务队列中的回调函数)

2) 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中

3) 宏任务执行完毕后,立即执行当前微任务队列中的所有任务(依次执行)

4) JS引擎线程挂起,GUI线程执行渲染

5) GUI线程渲染完毕后挂起,JS引擎线程执行任务队列中的下一个宏任务

说明:

1) 当某个宏任务执行完后,会查看是否有微任务队列。

2) 如果有,先执行微任务队列中的所有任务,

3) 如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。

4) 栈空后,再次读取微任务队列里的任务,依次类推。

推荐热门技术文章:

干货篇
  • 小程序踩坑之旅

  • 干货:开启 “ 移动端开发踩坑” 之旅

  • 干货:“一套代码,三屏齐发”——响应式开发浅析

  • vue简单的axios二次封装

  • 解锁 Webpack,看这篇就够了

  • 关于移动端适配,你必须要知道的

基础篇
  • 干货:前端开发常见规范

  • 这一次,彻底弄懂 Promise

  • 你还是只会 npm install 吗?

  • 面试官:你真的彻底搞懂CSS了吗?

  • CSS预处理语言—sass与less的使用

  • 初探 Electron 桌面应用 - 理论篇

  • 一篇文章了解 JsBridge

工具篇
  • 这样配置,让你的VS Code好用到飞起!

  • 一篇文章,教你学会Git

  • 性能优化篇 - Performance(工具 & api)

  • 让程序员变懒的 “vue-admin-template” 后台管理系统

优化篇
  • 前端性能优化实战

  • 解析Web缓存及其最佳实践

  • 开启 flex 与 grid 布局方式之旅

进阶篇
  • Babel插件入门-AST(抽象语法树)

  • 彻底弄懂 “ 防抖 和 节流 ”

  • 一文读懂,关于 “ HTTP ” 那些事

  • 微服务开发之 gRPC 通信

觉得本文对你有帮助?请分享给更多人

关注「前端学苑」加星标,提升前端技能

5b34e2a05e176015600c636878208e58.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值