0%

浏览器的 Event Loop 机制

浏览器的 Event Loop 机制

浏览器的多线程

浏览器是事件驱动的,浏览器中很多行为是异步的,会创建事件并放入执行队列中。

  • 浏览器的渲染进程:
    1. GUI 线程
    2. JS 引擎线程
    3. 定时器触发线程
    4. 事件触发线程
    5. 异步HTTP请求线程

JavaScript 是单线程的

JavaScript 是单线程的,也就意味着它只有一个主线程,一次只能执行一段代码。

  • JavaScript 的运行机制

    1. 所有同步任务都在主线程上执行,形成一个执行栈。
    2. 主线程之外,还存在”任务队列“。只要异步任务有了运行结果,就在”任务队列“之中放置一个事件。
    3. 一旦”执行栈“中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    4. 主线程不断重复上面的第三步。
  • JavaScript 中有两种异步任务:

    1. 宏任务:script, setTimeout/setInterval, setImmediate, I/O, UI rendering
    2. 微任务:Promises, Object.observe, MutationObserver, postMessage

什么是Event Loop?

主线程从”任务队列“中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环(Event Loop)。主线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查 microtask 队列是否为空(执行完一个任务的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有 microtask 。然后再进入下一个循环去任务队列中取下一个任务执行。

  • 具体过程:
    1. 先执行全局 script 任务,执行完(主线程执行栈空)
    2. 检查微任务队列里的所有任务,依次执行,全部执行完毕
    3. 检查宏任务,读取第一个宏任务执行(执行宏任务的时候可能产生了新的微任务)
    4. 重复步骤2、3,直到所有任务都执行完毕,执行栈为空。

实例解析

以练习题1为例解析 Event Loop 的执行流程:

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
async function async1() {
console.log('1');
console.log(await async2());
console.log('2');
}
async function async2() {
console.log('3');
return '0';
}
setTimeout(function () {
console.log('4');
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('6')
})
});
async1();
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8');
});
console.log('9');
  • 循环一:

    • 【task队列:script ;microtask 队列:】
    1. 从 task 队列中取出 script 任务,推入栈中执行
    2. setTimeout 列为 task
    3. 执行 async1 函数
    4. 打印1
    5. 执行 async2 函数的 executor 代码
    6. 打印3
    7. async2 回调列为 microtask
    8. 执行 Promise 的 executor 代码
    9. 打印7
    10. Promise 回调列为 microtask
    11. 打印9
    • 【task队列:setTimeout ;microtask 队列:async2 回调,Promise 回调】
    1. script 任务执行完毕,执行 microtask checkpoint
    2. 取出 async2 回调执行
    3. 打印0
    4. 打印2
    5. 取出 Promise 回调执行
    6. 打印8
  • 循环二:

    • 【task队列:setTimeout ;microtask 队列:】
    1. 微任务执行完毕,从宏任务队列中取出 setTimeout,推入栈中执行
    2. 打印4
    3. 执行 Promise 的 executor 代码
    4. 打印5
    5. Promise 返回进入微任务队列
    • 【task队列:;microtask 队列:Promise 回调】
    1. setTimeout 任务执行完毕,执行 microtask checkpoint
    2. 取出 Promise 回调执行
    3. 打印6
    • 【task队列:;microtask 队列:】

#开发笔记/JavaScript进阶/异步编程