Javascript中的事件循环

本文是笔者花了点时间重新学习了下 javascript 中的事件循环总结的个人博客,本文的例子是网上摘录,仅供学习哈~

认识 Event Loop

Javascript 语言的最大特点就是单线程,这也就意味着引擎只有在执行完前一个任务后才会执行后一个任务。就算前置任务耗时长,后续任务也不能执行,就想现实中的飞行航班的前序航班延误,那么后续航班也会因此延误。

正是因为此特性,所以 JS 中有了任务队列这说法,任务队列是一个先进先出的数据结构,前面的事件会优先被主线程读取。主线程循环不断从任务队列中读取事件执行这种运行机制就叫 Event Loop。

任务又分为同步任务(synchronous)和异步任务(asynchronous)。同步任务是指在主线程上排队待执行的任务;异步任务是指不进入主线程,而进入任务队列的任务。当异步任务可以执行时才进入任务队列,等待主线程空闲时读取执行。

运行机制:

  1. 主线程依次执行所有同步任务,形成一个任务执行栈。
  2. 主线程如果执行异步任务,任务有结果之后,将回调事件加入任务队列。
  3. 主线程所有同步任务执行完毕,系统依次读取任务队列事件加入执行栈并执行,如果有新的异步事件会形成新的任务队列。
  4. 主线程重复直至所有任务执行完毕。

不同的异步任务也被分为两类:微任务(micro task)和宏任务(macro task)。

宏任务:

  • setTimeout()
  • setInterval()
  • setImmediate (Nodejs 环境)

微任务:

  • promise.[then/catch/finally]
  • process.nextTick(Nodejs 环境)
  • Object.observe

在一个事件循环中,异步事件返回结果会被放到任务队列中,根据异步事件的类型会被放入对应的宏任务队列或者微任务队列。当当前执行栈为空时,主线程会查看微任务队列并执行所有任务直至微任务队列为空,然后从宏任务队列取出一个事件推入执行栈,如此反复。

同一次事件循环中,微任务永远在宏任务前执行。

几个例子

举例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
console.log('script start');
async function async1() {
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 end');
}
setTimeout(function () {
console.log('setTimeout');
}, 0);
new Promise((resolve) => {
console.log('Promise');
resolve();
})
.then(function () {
console.log('promise1');
})
.then(function () {
console.log('promise2');
});
async1();
console.log('script end');

结果:

1
2
3
4
5
6
7
8
script start
Promise
async2 end
script end
promise1
async1 end
promise2
setTimeout

举例 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
console.log('sync statement 1');
Promise.resolve()
.then(function () {
console.log('micro task 1');
setTimeout(function () {
console.log('macro task 1');
}, 0);
})
.then(function () {
console.log('micro task 2');
});

setTimeout(function () {
console.log('macro task 2');
Promise.resolve().then(function () {
console.log('micro task 3');
});
}, 0);
console.log('sync statement 2');

结果:

1
2
3
4
5
6
7
sync statement 1
sync statement 2
micro task 1
micro task 2
macro task 2
micro task 3
macro task 1

举例 3:

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
console.log('1');
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4');
});
}, 0);
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('6');
});
setTimeout(function () {
console.log('7');
new Promise(function (resolve) {
console.log('8');
resolve();
}).then(function () {
console.log('9');
});
});

结果:

1
2
3
4
5
6
7
8
9
1
5
6
2
3
4
7
8
9