浏览器的 Event Loop 机制
浏览器的多线程
浏览器是事件驱动的,浏览器中很多行为是异步的,会创建事件并放入执行队列中。
- 浏览器的渲染进程:
- GUI 线程
- JS 引擎线程
- 定时器触发线程
- 事件触发线程
- 异步HTTP请求线程
JavaScript 是单线程的
JavaScript 是单线程的,也就意味着它只有一个主线程,一次只能执行一段代码。
JavaScript 的运行机制
- 所有同步任务都在主线程上执行,形成一个执行栈。
- 主线程之外,还存在”任务队列“。只要异步任务有了运行结果,就在”任务队列“之中放置一个事件。
- 一旦”执行栈“中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
JavaScript 中有两种异步任务:
- 宏任务:script, setTimeout/setInterval, setImmediate, I/O, UI rendering
- 微任务:Promises, Object.observe, MutationObserver, postMessage
什么是Event Loop?
主线程从”任务队列“中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环(Event Loop)。主线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查 microtask 队列是否为空(执行完一个任务的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有 microtask 。然后再进入下一个循环去任务队列中取下一个任务执行。
- 具体过程:
- 先执行全局 script 任务,执行完(主线程执行栈空)
- 检查微任务队列里的所有任务,依次执行,全部执行完毕
- 检查宏任务,读取第一个宏任务执行(执行宏任务的时候可能产生了新的微任务)
- 重复步骤2、3,直到所有任务都执行完毕,执行栈为空。
实例解析
以练习题1为例解析 Event Loop 的执行流程:
1 | async function async1() { |
循环一:
- 【task队列:script ;microtask 队列:】
- 从 task 队列中取出 script 任务,推入栈中执行
- setTimeout 列为 task
- 执行 async1 函数
- 打印1
- 执行 async2 函数的 executor 代码
- 打印3
- async2 回调列为 microtask
- 执行 Promise 的 executor 代码
- 打印7
- Promise 回调列为 microtask
- 打印9
- 【task队列:setTimeout ;microtask 队列:async2 回调,Promise 回调】
- script 任务执行完毕,执行 microtask checkpoint
- 取出 async2 回调执行
打印0
- 打印2
- 取出 Promise 回调执行
- 打印8
循环二:
- 【task队列:setTimeout ;microtask 队列:】
- 微任务执行完毕,从宏任务队列中取出 setTimeout,推入栈中执行
- 打印4
- 执行 Promise 的 executor 代码
- 打印5
- Promise 返回进入微任务队列
- 【task队列:;microtask 队列:Promise 回调】
- setTimeout 任务执行完毕,执行 microtask checkpoint
- 取出 Promise 回调执行
- 打印6
- 【task队列:;microtask 队列:】
#开发笔记/JavaScript进阶/异步编程