浏览器的Event-Loop

10次阅读

共计 907 个字符,预计需要花费 3 分钟才能阅读完成。

进程与线程

进程和线程都是 CPU 工作时间片的一个描述。
进程描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序。
线程是进程中的更小单位,描述了执行一段指令所需的时间。
JS 是单线程执行的。
把这些概念放到浏览器中来说,当你打开一个 Tab 页面,其实就是创建了一个进程,一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁。
在 JS 运行的时候可能会阻止 UI 渲染,这说明了两个线程是互斥的。这其中的原因是因为 JS 可以修改 DOM,如果在 JS 执行的时候 UI 线程还在工作,就可能导致不能安全的渲染 UI。
得益于 JS 是单线程运行的,可以达到节省内存,节约上下文切换时间,没有锁的问题的好处。

执行栈

可以把执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。

浏览器中的 Event Loop

当遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task)队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为微任务(microtask)和宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。
Event Loop 执行顺序如下:

  • 首先先执行同步任务,这属于宏任务;
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行;
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面;
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码。

微任务:process.nextTick,promise,MutationObserve。
宏任务:script,setTimeout,setInterval,setImmediate,I/O,UI rendering。
有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script,浏览器会先执行一个宏任务,接下来有一步代码的话才会先执行微任务。

正文完
 0