Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 事件循环(含宏任务与微任务) #195

Open
toFrankie opened this issue Feb 26, 2023 · 0 comments
Open

JavaScript 事件循环(含宏任务与微任务) #195

toFrankie opened this issue Feb 26, 2023 · 0 comments
Labels
2019 2019 年撰写 JS 与 JavaScript、ECMAScript 相关的文章 前端 与 JavaScript、ECMAScript、Web 前端相关的文章 已过时 表示已过时、或不适用于最新版本,或有更好的替代方案等

Comments

@toFrankie
Copy link
Owner

toFrankie commented Feb 26, 2023

本文已过时,可移步《通过两个例子再探 Event Loop》。

JavaScript 特点

JavaScript 是单线程非阻塞的一门语言。

单线程意味着 JavaScript 代码执行时只有一个主线程去处理所有的任务,即同一时间只能做一件事情。

非阻塞表示当执行到异步任务时,主线程会挂起该异步任务,待异步任务返回结果时,再根据一定的规则去执行相应的回调。

思考: 为什么 JavaScript 要设计成单线程?

单线程是必要的,也是 Javascript 这门语言的基石。在最初的浏览器执行环境中,我们需要进行各种各样的 DOM 操作。如果 Javascript 是多线程的话,当两个线程同时对 DOM 进行一项操作,比如一个向其添加事件,另一个删除了这个 DOM 元素,此时该如何处理呢?因此,Javascript 选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。

事件循环(Event Loop)

JavaScript 是通过「事件循环」的实现非阻塞的。而事件循环是通过「任务队列」机制协调的。

在事件循环中,每进行一次循环操作称为 Tick,每一次 Tick 的任务处理是比较复杂的,主要步骤如下:

  1. 在本次 Tick 中选择最先进入队列的任务,如有则执行一次。
  2. 检查是否存在微任务(microtask),如有则执行,直至清空微任务队列(microtask queue)。
  3. 渲染页面。
  4. 主线程重复执行上述步骤。

Tick 需要了解的是:

  • JS 任务分为「同步任务」和「异步任务」。
  • 同步任务都在主线程上执行,形成一个执行栈。
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了结果,就在任务队列里面放置一个事件。
  • 一旦执行栈中所有同步任务执行完毕(JS 引擎空闲之后),就会去读取任务队列,将可运行的异步任务添加到可执行栈里面,开始执行。

任务分为「同步任务」和「异步任务」,其中异步任务又分为「宏任务」和「微任务」。

宏任务(Task)

每次执行栈的代码就是一个宏任务(包括每次从事件队列中获取的一个事件回调并放到执行中执行)。

浏览器为了能够使得 JS 内部宏任务与 DOM 任务有序地执行,会在宏任务执行结束之后,在下一个宏任务开始执行之前,对页面进行重新渲染。

当前宏任务 → 渲染页面 → 下一个宏任务 → ...

常见宏任务有:

  • setInterval
  • setTimeout
  • setImmediate(node.js)
  • XHR 回调
  • 事件回调(鼠标键盘事件)
  • indexedDB 数据库等 I/O 操作
  • 渲染 DOM

微任务(Microtask)

当前同步任务执行结束之后,在下一个宏任务之前(也在渲染 DOM 之前),立即执行的任务。

当前宏任务 → 当前微任务 → 渲染页面 → 下一个宏任务 → ...

常见微任务包括:

  • Promise 的 thencatchfinally 回调。
  • process.nextTick(node.js)
  • MutationObserver
  • Object.observe(已废弃)

运行机制

  1. 执行一个宏任务(执行栈中没有就从事件队列中获取)。
  2. 执行过程中如果遇到微任务,就将其添加到微任务的任务队列里面。
  3. 宏任务执行完毕之后,立即执行当前微任务队列里面的所有微任务(依次执行)。
  4. 当前宏任务执行完毕之后,开始检查渲染,然后 GUI 线程接管渲染(但是 UI render 不一定会执行,因为需要考虑 UI 渲染消耗的性能已经有没有 UI 变动)。
  5. 渲染完毕后,JS 线程继续接管,开始下一个宏任务(从事件队列中获取)。
  6. JS 不断重复以上步骤,直至所有任务执行完毕。(栈内存溢出也会终止执行)。

References

@toFrankie toFrankie added 前端 与 JavaScript、ECMAScript、Web 前端相关的文章 JS 与 JavaScript、ECMAScript 相关的文章 labels Feb 26, 2023
@toFrankie toFrankie added the 2019 2019 年撰写 label Apr 26, 2023
@toFrankie toFrankie added the 已过时 表示已过时、或不适用于最新版本,或有更好的替代方案等 label Aug 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2019 2019 年撰写 JS 与 JavaScript、ECMAScript 相关的文章 前端 与 JavaScript、ECMAScript、Web 前端相关的文章 已过时 表示已过时、或不适用于最新版本,或有更好的替代方案等
Projects
None yet
Development

No branches or pull requests

1 participant