300毫秒 js 响应时间_标梵互动分享js的工作原理和机制

这一章由标梵互动带领我们了解js的工作原理,只有了解了js的工作原理,才能写出更漂亮的代码,提高运行效率,解决开发中遇到的无法理解的问题。

d6201d4690ddc2f108e53282fa4458dc.png
标梵互动分享js的工作原理和机制

进程和线程。

进程是cpu资源分配的最小单位,它可以包含多个线程。浏览器是多进程的,打开的每个浏览器窗口都是一个进程。

线程是cpu调度的最小单位,同一进程中的所有线程共享程序的内存空间。

流程可以看成一个仓库,螺纹是可以运输的卡车。每个仓库都有自己的卡车为仓库服务(送货)。每个仓库可以同时被多辆卡车拉,但是每辆卡车只能同时做一件事,就是这次运输货物。所以很好理解。

渲染过程。

浏览器由四个进程组成:

主进程(Browserprocess),浏览器只有一个主进程,负责资源下载、界面显示等主要基本功能。

GPU进程,负责三维图形绘制。

第三方插件流程,负责第三方插件处理。

渲染过程(Rendererprocess),负责js执行、页面渲染等功能,也是本章的重点内容。

渲染过程主要包括GUI渲染线程、Js引擎线程、事件循环线程、定时器线程和http异步线程。

GUI渲染线程。

首先看看浏览器在获得网站资源后做了什么:

浏览器首先会对html代码进行解析(其实html代码的本质就是一个字符串),并将其转换成浏览器能识别的一个节点,生成一个DOMTree,也就是一个DOM树。

然后解析css生成CSSOM(CSS规则树)

结合DOMTree和CSSOM生成渲染树

GUI就是来做这个的。如果某些元素的颜色或背景色被修改,页面将重新绘制。如果修改了元素的大小,页面将会重排。当页面需要重排版和重排版时,图形用户界面经常会执行和绘制页面。

这里有个提示:Reflow比Repaint贵,那么js性能优化中如何避免Reflow和Repaint呢?

JS引擎线程。

js引擎线程是js内核,负责解析和执行js代码,也称为主线程。同时浏览器只能有一个js引擎线程运行JS程序,所以JS是单线程运行的。

需要注意的是,js引擎线程和GUI渲染线程同时只能有一个作业,js引擎线程会阻塞GUI渲染线程。

<html>

<body>

<div id="div1"> a </div>

<script>

document.getElementById('div1').innerHTML = 'b'

</script>

<div id='div2'> div2 </div>

</body>

</html>

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

事件循环线程

事件循环线程用来管理控制事件循环,并且管理着一个事件队列(task queue),当js执行碰到事件绑定和一些异步操作时,会把对应的事件添加到对应的线程中(比如定时器操作,便把定时器事件添加到定时器线程),等异步事件有了结果,便把他们的回调操作添加到事件队列,等待js引擎线程空闲时来处理。

定时器线程

由于js是单线程运行,所以不能抽出时间来计时,只能另开辟一个线程来处理定时器任务,等计时完成,把定时器要执行的操作添加到事件任务队列尾,等待js引擎线程来处理。这个线程就是定时器线程。

异步请求线程

当执行到一个http异步请求时,便把异步请求事件添加到异步请求线程,等收到响应(准确来说应该是http状态变化),把回调函数添加到事件队列,等待js引擎线程来执行。

Event Loop

上面介绍了渲染进程中的5个主要的线程,可能看完上面对各个线程简单的介绍,还有点不明白他们之间到底怎么协作工作的,下面山东标梵互动就从Event Loop的角度来聊一聊他们之间是怎样那么愉快合作的。

已经知道了js是单线程运行的,也知道js中有同步操作和异步操作。同步和异步大家应该很熟了,不多介绍。

同步操作运行在js引擎线程(主线程)上,会形成一个执行栈,而异步操作则在他们对应的异步线程上处理(比如:定时操作在定时器线程上;http请求则在异步请求线程上处理)。

而事件循环线程则监视着这些异步线程们,等异步线程们里面的操作有了结果(比如:定时器计时完成,或者http请求获取到响应),便把他们的毁掉函数添加到事件队列尾部,整个过程中执行栈、事件队列就构成Event Loop。

有关定时器(setTimeout、setInterval)的更多趣事

定时器会按照规定时间执行吗?

定时器是规定在一段时间之后执行一段代码,但是在js执行中不会准确无误的按照预期的时间去执行定时器里面的代码。

一个原因是W3C标准规定setTimeout中最小的时间周期是4毫秒,凡是低于4ms的时间间隔都按照4ms来处理。

其实还有一个重要的原因,如果仔细看上面的文章,大家应该会想到在js执行的时候,主线程碰到定时器的时候,是不会直接处理的,应该是先把定时器事件交给定时器线程去处理,这时主线程继续执行下面的代码,同时定时器线程开始计时处理,等到计时完毕,事件循环线程会把定时器要执行的操作放在事件队列末尾,等主线程空闲的时候再来执行事件队列里面的操作。

应该使用setTimeout还是setInterval

使用setTimeout模拟setInterval代码类似以下代码:

var say = function() {

setTimeout(say, 1000)

console.log('hello world')

}

setTimeout(say, 1000)

这样,当js遇到定时器时,就会交给定时器线程处理。计时完成后,计时器中的操作将被添加到事件队列中。当主线程空闲执行时,会遇到主线程执行时的定时器。这是上面的一系列操作。

你会发现这样做会在每次定时器执行的时候启动下一个定时器,错误只是等待主线程空闲的时间。

SetInterval就是定期把一个事件推入定时器线程,这就有一个问题,就是累积效应。

累积效应:

也就是说,如果在定时器中执行代码所需的时间比定时器的执行周期长,就会出现累积效应。简单来说就是上一次定时器中的操作没有完成,下一次定时器事件又来了。

累积效应会导致一些事件的丢失,以及丢失的原因。有兴趣的可以看看这篇文章,为了安全起见,尽量用setTimeout而不是setInterval。

如果有对setTimeout非常感兴趣的同学,我强烈建议你看一下80%考生不及格的JS面试题。

宏任务和微任务。

Microtask是Promise中的一个新概念。

宏任务.

宏任务中的事件都放在事件队列中,由事件触发线程维护。

宏任务(Macrotask),可以理解为执行栈每次执行的代码都是宏任务(包括每次从事件队列中获取一个事件回调,放入执行栈执行)

每个任务都会从头到尾的完成这个任务,不会执行其他任何事情。

为了让JS内部任务和DOM任务有序执行,浏览器会在一个任务执行完之后,下一个任务执行之前,重新渲染页面。

microtask.

microtask(也称为microtask)可以理解为在当前任务执行后立即执行的任务。

微任务中的所有微任务都被添加到由JS引擎线程维护的微任务作业队列中。

在当前任务之后、下一个任务之前和渲染之前执行。

所以它的响应速度比setTimeout(setTimeout是任务)快,因为不需要等待渲染。

也就是说,在宏任务执行之后,在其执行期间生成的所有微任务都将被执行(在渲染之前)

因此,js运行过程:

执行宏任务(如果它不在堆栈中,则从事件队列中获取它)

如果在执行过程中遇到微任务,它将被添加到微任务的任务队列中。

宏任务执行后,立即执行当前微任务队列中的所有微任务(按顺序执行)

当前宏任务完成后,检查渲染,然后GUI线程接管渲染。

渲染后,JS线程继续接管并启动下一个宏任务(从事件队列中获取)

宏任务和微任务的分析借鉴了浏览器多进程到JS单线程的JS运行机制最全面的梳理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值