参考
js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段。
上篇文章我们介绍了语法分析和预编译阶段,做个简单概括,如下:
- 语法分析: 分别对加载完成的代码块进行语法检验,语法正确则进入预编译阶段;不正确则停止该代码块的执行,查找下一个代码块并进行加载,加载完成再次进入该代码块的语法分析阶段。
- 预编译:通过语法分析阶段后,进入预编译阶段,则创建变量对象(创建arguments对象(函数运行环境下),函数声明提前解析,变量声明提升),确定作用域链以及this指向。
本文主要分析js引擎执行的第三个阶段–执行阶段,在分析之前我们先思考以下两个问题:
1、js是单线程的,为了避免代码解析阻塞使用了异步执行,那么它的异步执行机制是怎么样的?
答:通过事件循环(Event Loop),理解了事件循环的原理就理解了js的异步执行机制
2、js是单线程的,那么是否代表参与js执行过程的线程就只有一个?
答:不是的,会有四个线程参与该过程,但是永远只有JS引擎线程在执行JS脚本程序,其他的三个线程只协助,不参与代码解析与执行。参与js执行过程的线程分别是:
(1)JS引擎线程: 也称为JS内核,负责解析执行Javascript脚本程序的主线程(例如V8引擎)。
(2)事件触发线程: 归属于浏览器内核进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数推进事件队列,等待JS引擎线程执行。
(3)定时器触发线程:主要控制计时器setInterval和延时器setTimeout,用于定时器的计时,计时完毕,满足定时器的触发条件,则将定时器的处理函数推进事件队列中,等待JS引擎线程执行。
注:W3C在HTML标准中规定setTimeout低于4ms的时间间隔算为4ms。
(4)HTTP异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。
注:浏览器对同一域名请求的并发连接数是有限制的,Chrome和Firefox限制数为6个,ie8则为10个。
总结:永远只有JS引擎线程在执行JS脚本程序,其他三个线程只负责将满足触发条件的处理函数推进事件队列,等待JS引擎线程执行
。
执行阶段
先分析一个典型的例子:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
直接划分例子的代码结构,简单描述分析执行过程;
1、宏任务(macro-task)
宏任务又按执行顺序分为同步任务和异步任务
(1)同步任务