为什么要了解JS执行机制?
JS执行机制是js执行的底层原理,理解了底层原理对于开发方法效率会提高,调错都效率也会增加很多。在未来发展道路上,了解底层才能够在技术的道路上走得更远!下面来和总结一下js执行机制原理吧。我从浏览器常驻的线程逐一说起。
浏览器常驻的线程
- js引擎线程(js主线程)
js执行是单线程,也就是说同一时间内只能做一件事(同步),但是js中也包含一些异步的事情,比如我们下班介绍的一些。 - GUI线程(绘制用户界面与JS主线程是互斥)
这个线程大家听起来可能比较陌生,但是这是大家一直在开发中看到的东西。大家回忆一些button按钮,我们点击时按钮颜色的变化,这个线程就是由GUI线程决定的! - http网络请求线程(处理用户GET POST请求,等返回结果后将回调函数推入到任务队列当中)
比如ajax的请求就是异步,和js主线程也是互斥的! - 定时触发器线程(setTimeout,setInterval等待时间结束后把执行函数推入任务队列当中)
- 浏览器事件处理线程(将 click、mouse 等交互事件发生后将这些事件放入事件队列中)
以上的GUI线程,http网络请求和定时器,浏览器事件处理线程均是异步的和js主线程是互斥的。
JS执行机制
js是单线程,意思是js在执行代码的时候那么其他的任务就不能做。但是当有异步的任务时发生的步骤也是非常重要的。
JavaScript 是基于单线程运行的,同时又是可以异步执行的,一般来说这种既是单线程又是异步的语言都是基于事件来驱动的,恰好浏览器就给 JavaScript 提供了这么一个环境
js执行的的顺序:
- 判断是同步任务还是异步任务
- 同步的话放入js主线程执行栈中,异步的先放入事件注册当中(Event Table)
- 比如异步的时间是ajax请求,让请求成功的时候,异步的函数并不会执行执行而是放入到任务队列(Event queue)当中的第一位。如果还有异步任务比如定时器,当定时器的时间到的话,放入到任务队列当中的第二位。
- 当js主线程执行完任务,执行栈为空的时候,会去任务队列(Event queue)中看是否有其他任务,按顺序(先进先出)依次放到执行栈中执行,先执行ajax,然后执行完主线程为空,再去Event queue中拿定时器执行。执行完在去看任务队列中是否有,形成事件循环(Event loop)。
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入 Event Table 并注册函数。
当指定的事情完成时,Event Table 会将这个函数移入 Event Queue 。
主线程的执行任务完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的 Event Loop (事件循环)。
下面来一张流程图,这样看起来更加的清晰!
在这里需要注意的是一开始一定的先执行js主线程中的东西,执行完之后才去任务队列中拿任务到js主线程中继续执行。如果js主线程是一个死循环呢?那么他后面的异步任务还会执行吗?当然不会,js主线程执行不完,其他异步任务不可能触发执行,一直在任务队列中等待js主线程运输执行。
<script>
while(true){
console.log("Ahua");
}
setTimeout(function () {
console.log(123);
},100)
//永远都不会打印123 同时这也是造成定时器不准的一个原因 本来100ms就应该执行定时器,现在却永远不会执行。因为js主线程始终执行不完
</script>
JS引擎线程和GUI线程互斥
JS可以操作DOM元素,进而影响到GUI的渲染结果,因此JS引擎线程与GUI线程是互斥的。也就是说当JS主线程处于运行状态时,GUI渲染线程将处于冻结状态。
JS执行机制多线程不好吗
js主要做的工作是用户交互体验,处理DOM,如果js是多线程的,那么想这么一个场景。在同一时间,一个线程想要修改DOM,另外一个元素想要删除这个DOM,浏览器不知道听谁的,如果引入锁的机制,那么就和其他语言一样尴尬了! 所以JS主线程还是非常棒的!
单线程计算能力有限,如果有大量数据操作怎么办?
如果有大量数据操作的话,那么就需要配合后端操作了,VUE月nodejs配合也就是SSR技术。
同步任务
同步任务
执行栈------相当于 js 主线程
function foo(ot){
function bar(it){
console.log(it); // 先输出20
}
bar(20);
console.log(ot); // 后输出10
}
foo(10);
-
代码没有执行的时候,执行栈为空栈
-
foo 函数执行时,创建了一帧,帧中包含了形参、局部变量 (预编译过程) ,把帧压入栈中
-
执行 foo 函数内代码,执行 bar 函数
-
创建新帧,同样有形参、局部变量,压入栈中
-
bar 函数执行完毕,弹出栈
-
foo 函数执行完毕,弹出栈
-
执行栈为空
异步任务
$.ajax({
url:'localhost:/js/domo.json',
data:{},
success:function(data){
console.log(data);
}
});
console.log('run');
-
Ajax 进入 Event Table,注册回调函数 success,执行 console.log( ‘run’ )
-
Ajax 事件完成 http 网络请求线程把任务放入 Event Queue 中
-
主线程( 调用栈 )读取任务执行 success 函数
关于定时器 setTimeout,setInterval
js 引擎底层是红黑树,当程序执行时,定时器会将其推送到任务队列中,只有当 js 的同步任务全部执行完毕之后,才会进行时间间隔,执行定时器,基于以上两点,定时器是不准确的
setTimeout的等待时间结束后并不是直接执行的而是先推入浏览器 的一个任务队列,在同步队列结束后在依次调用任务队列中的任务。
setTimeout(function(){}, 0)Js主线程中的执行栈为空时,0毫秒实际上也达不到的,根据HTML标准,最低4毫秒。
setInterval是每隔一段时间把任务放到Event Queue之中
function test (num) {
for (var i = 0; I < num; i++) {
console.log(i);
}
}
setTimeout(function () {console.log(‘time’)}, 400);