Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【JS】JS的运行机制 #104

Open
Easay opened this issue May 26, 2021 · 2 comments
Open

【JS】JS的运行机制 #104

Easay opened this issue May 26, 2021 · 2 comments
Labels

Comments

@Easay
Copy link
Owner

Easay commented May 26, 2021

JS为什么设计成单线程

JS是单线程与它的用途有关,作为浏览器脚本,JS主要用途是与用户互动,以及操作DOM,这决定了JS只能是单线程的。假定JS同时有两个线程,一个改变某个DOM节点的内容,另一个删除这个DOM节点,则会发生冲突和错误。

为了利用多核CPU的计算能力,HTML5提出了web worker标准,允许JS创建多个线程,但子线程完全受主线程控制,且不得操作DOM。所以也没有改变JS单线程的本质。

JS为什么有Event Loop

通过将异步任务放入队列中,可以先执行同步任务,防止一直处于等待状态。影响用户体验。

@Easay Easay added the JS label May 26, 2021
@Easay
Copy link
Owner Author

Easay commented May 27, 2021

参考:http://kmanong.top/kmn/qxw/form/article?id=69913&cate=51

进程与线程

进程是CPU资源分配的最小单位。

一个可以独立运行且拥有自己的资源空间的任务程序。进程包括运行中的程序和程序所使用到的内存和系统资源。CPU在运行一个进程时,其他的进程处于非运行状态,CPU使用时间片轮转调度算法来实现同时运行多个进程。

线程是CPU调度的最小单位。

线程是建立在进程的基础上的一次程序运行单位,通俗点解释线程就是程序中的一个执行流,一个进程可以有多个线程。

一个进程中只有一个执行流称作单线程,即程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

进程之间相互独立,但同一进程下的各个线程间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)

浏览器

浏览器是多进程的

拿Chrome来说,我们每打开一个Tab页就会产生一个进程,我们使用Chrome打开很多标签页不关,电脑会越来越卡,不说其他,首先就很耗CPU。

Chrome为什么要使用多进程架构?

如果浏览器中的一个tab网页崩溃的话,将会导致其他被打开的网页应用崩溃。进程之间是不共享资源和地址空间的,提高了安全性。

浏览器包含哪些进程

  • Browser进程
浏览器的主进程(负责协调、主控),该进程只有一个
负责浏览器界面显示,与用户交互。如前进,后退等
负责各个页面的管理,创建和销毁其他进程
将渲染(Renderer)进程得到的内存中的Bitmap(位图),绘制到用户界面上
网络资源的管理,下载等
  • 第三方插件进程
    每种类型的插件对应一个进程,当使用该插件时才创建
  • GPU进程
    该进程也只有一个,用于3D绘制等等。
  • 渲染进程
即通常所说的浏览器内核(Renderer进程,内部是多线程)
每个Tab页面都有一个渲染进程,互不影响
主要作用为页面渲染,脚本执行,事件处理等

页面的渲染、JS的执行、事件循环都在渲染进程内执行。渲染进程是多线程的。

渲染进程Renderer

GUI渲染线程

负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。GUI渲染线程与JS引擎线程是互斥的。

当JS引擎执行时GUI线程会被挂起(相当于被冻结了);GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

JS引擎线程

JS引擎线程就是JS内核,负责处理Javascript脚本程序(例如V8引擎)。JS引擎线程负责解析Javascript脚本,运行代码。浏览器同时只能有一个JS引擎线程在运行JS程序,所以js是单线程运行的。一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序。

例如浏览器渲染的时候遇到<script>标签,就会停止GUI的渲染,然后js引擎线程开始工作,执行里面的js代码,等js执行完毕,js引擎线程停止工作,GUI继续渲染下面的内容。所以如果js执行时间太长就会造成页面卡顿的情况。

事件触发线程

  • 属于浏览器而不是JS引擎,用来控制事件循环,并且管理着一个事件队列(task queue)
  • 当js执行碰到事件绑定和一些异步操作(如setTimeOut,也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会走事件触发线程将对应的事件添加到对应的线程中(比如定时器操作,便把定时器事件添加到定时器线程),等异步事件有了结果,便把他们的回调操作添加到事件队列,等待js引擎线程空闲时来处理。
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。
  • 因为JS是单线程,所以这些待处理队列中的事件都得排队等待JS引擎处理。

定时触发器线程

  • setInterval与setTimeout所在线程。
  • 浏览器定时计数器并不是由JavaScript引擎计数的(因为JavaScript引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确)
  • 通过单独线程来计时并触发定时(计时完毕后,添加到事件触发线程的事件队列中,等待JS引擎空闲后执行),这个线程就是定时触发器线程,也叫定时器线程
  • W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms

异步Http请求线程

  • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
  • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中再由JavaScript引擎执行
  • 简单说就是当执行到一个http异步请求时,就把异步请求事件添加到异步请求线程,等收到响应(准确来说应该是http状态变化),再把回调函数添加到事件队列,等待js引擎线程来执行

@Easay
Copy link
Owner Author

Easay commented May 27, 2021

首先,整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为同步任务、异步任务两部分

同步任务会直接进入主线程依次执行

异步任务会再分为宏任务和微任务

宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中

微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中

当主线程内的任务执行完毕,主线程为空时,会检查微任务的Event Queue,如果有任务,就全部执行,如果没有就执行下一个宏任务

上述过程会不断重复,这就是Event Loop,比较完整的事件循环

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant