js运行机制

      本文主要是记录下自己对js运行机制的理解。js是一门单线程语言。也就是说,同一时间只能做一件事。JS的主要用途是与用户互动以及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。假定js同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,那么浏览器应该以哪个线程为准呢?所以为了避免复杂性,提高效率,就决定了js是一门单线程语言,这已经成了这门语言的核心特征。Event Loop是 javascript 的执行机制。

js 是如何处理任务

      js中所有任务可以分成两种,一种是同步任务,一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。


javascript事件循环

具体来说, js的处理任务机制如下:

(1)同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入 Event Table 并注册函数。

(2)当指定的事情完成时,Event Table 会将这个函数移入 Event Queue。

(3)主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。

(4)上述过程会不断重复,也就是常说的 Event Loop(事件循环)。

下面我们来看一段代码帮助我们更好的理解:

1
2
3
4
5
6
console.log(1);
setTimeout(function() {
console.log("我是定时器");
}, 0);
console.log(2);
//输出结果:1,2,我是定时器

(1) 进入主线程,遇到console.log,输出 1

(2) 遇到setTimeout,其回调函数被分发到 Event Queue 中。

(3) 遇到console.log(2),输出 2。

(4) 主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行

微任务(Microtask)与宏任务(Macrotask)

      异步任务分为宏任务和微任务,宏任务队列可以有多个,微任务队列只有一个。

  • 宏任务包括:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • 微任务包括: new Promise().then(回调), process.nextTick, Object.observe(已废弃), MutationObserver(html5 新特性)

宏任务,微任务的关系

      当某个宏任务队列的中的任务全部执行完以后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,就查看是否有其他宏任务队列。

接下来我们来看个复杂的例子来彻底理解下 js 的运行机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
console.log("1");

setTimeout(function() {
console.log("2");
process.nextTick(function() {
console.log("3");
});
new Promise(function(resolve) {
console.log("4");
resolve();
}).then(function() {
console.log("5");
});
});
process.nextTick(function() {
console.log("6");
});
new Promise(function(resolve) {
console.log("7");
resolve();
}).then(function() {
console.log("8");
});

setTimeout(function() {
console.log("9");
process.nextTick(function() {
console.log("10");
});
new Promise(function(resolve) {
console.log("11");
resolve();
}).then(function() {
console.log("12");
});
});
//完整的输出为1,7,6,8,2,4,3,5,9,11,10,12

第一轮事件循环流程分析如下:

(1) 整体 script 作为第一个宏任务进入主线程,遇到 console.log,输出 1。

(2) 遇到 setTimeout,其回调函数被分发到宏任务 Event Queue 中。我们暂且记为 setTimeout1。

(3) 遇到 process.nextTick(),其回调函数被分发到微任务 Event Queue 中。我们记为 process1。

(4) 遇到 Promise,new Promise 直接执行,输出 7。then 被分发到微任务 Event Queue 中。我们记为 then1。

(5) 又遇到了 setTimeout,其回调函数被分发到宏任务 Event Queue 中,我们记为 setTimeout2。

(6) 第一轮事件循环宏任务结束时各 Event Queue 的情况,此时已经输出了 1 和 7

(7) 我们发现了 process1 和 then1 两个微任务执行 process1,输出 6。

(8) 执行 then1,输出 8

第二轮事件循环流程分析如下:

(1)从 setTimeout1 宏任务开始。首先输出 2。

(2) 接下来遇到了 process.nextTick(),同样将其分发到微任务 Event Queue 中,记为 process2。

(3) new Promise 立即执行输出 4,then 也分发到微任务 Event Queue 中,记为 then2。

(4) 第二轮事件循环宏任务结束,我们发现有 process2 和 then2 两个微任务可以执行。

(5) 输出 3。

(6) 输出 5

第三轮事件循环流程分析如下:

(1)第三轮事件循环开始,此时只剩 setTimeout2 了,直接输出 9。

(2) 将 process.nextTick()分发到微任务 Event Queue 中。记为 process3。

(3) 直接执行 new Promise,输出 11。

(4) 将 then 分发到微任务 Event Queue 中,记为 then3。

(5) 第三轮事件循环宏任务执行结束,执行两个微任务 process3 和 then3。

(6) 输出 10

(7) 输出 12

Copyright ©2019 guowj All Rights Reserved.

访客数 : | 访问量 :