前端实践-原理-事件循环 | 豆包MarsCode AI 刷题

57 阅读4分钟

在前端开发中,事件的执行顺序是挺重要的,要了解事件循环,需要先了解线程和进程的概念。

进程和线程

什么是进程?

进程是一个正在执行的程序实例,它拥有独立的内存空间。在操作系统中,每个程序都有自己的进程,每个进程之间互相独立,彼此之间的通信需要经过特殊的协商和管理。

什么是线程?

线程是进程中的执行单元,一个进程至少包含一个线程,这个线程被称为“主线程”。当程序需要并发执行多个任务时,主线程会创建更多的线程来处理其他任务。因此,进程可以包含多个线程,多个线程可以共享进程的资源。

浏览器中的进程和线程

浏览器是一个多进程、多线程的应用程序。为了提高性能和稳定性,浏览器通常会启动多个进程来分担不同的任务。常见的浏览器进程包括:

  1. 浏览器进程:负责界面渲染、用户交互以及管理其他进程。
  2. 网络进程:负责网络资源的加载和处理。
  3. 渲染进程:负责页面渲染,它会创建一个渲染主线程来执行页面的 HTML、CSS 和 JavaScript 代码。

202208092131410.png

通常,每个标签页都会启动一个独立的渲染进程,以确保不同标签页之间互不干扰。

渲染主线程的任务

  • 解析 HTML、CSS 和 JavaScript
  • 执行样式计算和布局
  • 绘制页面元素
  • 处理用户事件(如点击、输入)
  • 执行定时器回调函数等

异步渲染

浏览器通过异步执行来避免主线程阻塞。对于一些需要等待的任务,如网络请求、定时器等,浏览器不会让主线程等待它们完成,而是将这些任务交给其他线程处理。当任务完成时,回调函数会被放入消息队列等待执行。这样,主线程可以继续执行其他任务,保持响应速度。

引入:如何处理多任务

渲染进程中的主线程需要处理许多不同的任务,这就带来了一个问题:如何合理安排任务的执行顺序?例如:

  • 当正在执行一个 JavaScript 函数时,如果用户点击了按钮,是否应该立即响应点击事件?
  • 当一个计时器的时间到期时,是否应立刻执行回调函数?

为了解决这个问题,浏览器使用了消息队列和事件循环机制。渲染主线程通过以下方式管理任务:

  1. 渲染主线程处于一个无限循环中,不断检查消息队列。
  2. 每次循环时,如果消息队列中有任务,主线程会取出并执行它们。如果没有任务,则主线程会休眠。
  3. 当其他线程向队列添加任务时,主线程会被唤醒,继续执行任务。

这种方式保证了浏览器的主线程始终保持活跃,并且每个任务都能按顺序得到执行。

202208101048899.png

任务的优先级顺序

任务队列按照优先级分为不同的队列,其中微任务队列的优先级最高。现代浏览器会先执行微任务队列中的任务,再执行宏任务队列中的任务。例如,PromiseMutationObserver 的回调会被放入微任务队列,而 setTimeout 和事件处理回调则会被放入宏任务队列。

补充:JS定时器的进度

虽然浏览器提供了 setTimeoutsetInterval 来进行定时任务调度,但这些定时器的精确度并不高。

  1. 计算机硬件的时钟精度有限。
  2. 操作系统对时间的管理会有微小的误差。
  3. 浏览器事件循环和任务调度的机制也会影响定时器的执行时间。

因此,定时器的执行时间可能会有一定的偏差,尤其是在嵌套定时器或任务较多时,延迟可能更明显。