我会分为两部分来深入剖析一下JavaScript的底层运行机制。在这篇文章主要是从浏览器常驻线程 和 eventLoop 来了解js的运行机制。在第二篇文章会从es6的promise和es7的async、await来了解js的运行机制。
一、浏览器常驻线程
浏览器的常驻线程有5个,分别是Js引擎线程、GUI线程、http网络请求线程、定时触发器线程、浏览器时间处理线程。它们的作用分别如下:
1.Js引擎线程:解释执行js代码、用户输入、网络请求
2.GUI线程:绘制用户界面、与js主线程是互斥的
3.http网络请求线程:处理用户的get\post等请求,等返回结果后将回调函数推入任务队列
4.定时触发器线程:setTimeout、setInterval等待时间结束后把执行函数推入任务队列中
5.浏览器时间处理线程:将click、mouse等交互事件发生后将这些事件放入事件队列中
http网络请求线程、定时触发器线程、浏览器时间处理线程会监听事件的执行,待事件响应将回调函数放进事件队列。要特别注意的是js引擎线程和GUI渲染线程是互斥的,即在js引擎线程在进行的话,画面是不会渲染的。
下面我着重讲解一下js引擎线程,即我们的js运行机制。
二、Js执行机制
JavaScript是基于单线程运行的,同时又是可以异步执行的,一般来说这种既是单线程又是异步的语言都是基于事件来驱动的,恰好浏览器就给JavaScript提供了这么一个环境。先看一下这张开门见山的图片,大概了解一下js的执行机制是怎么样的。
我们来分析一下这张图片
1.主线程自上而下执行所有代码
2.同步任务直接进入到主线程被执行,而异步任务则进入到Event Table并注册相对应的回调函数
3.异步任务完成后,Event Table会将这个函数移入Event Queue(FIFO)
4.主线程任务执行完了以后,会从Event Queue中读取任务,进入到主线程去执行。
这个过程会一直循环,这就是我们所说的事件循环(Event Loop)
下面我会举同步任务和异步任务的执行过程的例子
同步任务
function foo(ot){
function bar(it){
console.log(it);
}
bar(20);
console.log(ot);
}
foo(10);
这是一个典型的同步任务,它是怎样进行的呢,我用文字来详细描述一下它的过程。执行栈即为js的主线程
a、代码没有执行的时候,执行栈为空栈。
b、foo函数执行时,创建一帧,这帧中包含了形参、局部变量(预编译过程),然后把这一帧压入栈中。
c、然后执行foo函数内代码,执行bar函数
d、创建新帧,同样有形参、局部变量、压入栈中
e、bar函数执行完毕,弹出栈
f、foo函数执行完毕,弹出栈
g、执行栈为空
异步任务
$.ajax({
url:'',
data:{},
success:function(data){
console.log(data)
}
})
console.log('run')
分析一下其过程
1.Ajax进入Event Table,注册回调函数success
2.执行console.log(“run”)
3.ajax事件完成,http网络请求线程把任务放入Event Queue中
4.主线程(调用栈)为空时读取事件队列的任务,执行success函数
重新理解定时器
1.setTimeout的等待时间结束后并不是直接执行的,而是定时触发器线程将该任务先推入浏览器的事件队列中,在同步队列结束后再依次调用任务队列中的任务。
2.setInterval是每隔一段时间把任务队列放到Event Queue之中。