事件循环
单线程的 JavaScript 同步与异步 JavaScript 在设计之初是作为浏览器脚本语言,主要用于与用户进行页面交互和操纵 DOM。因此,为避免由不可预测的用户操作可能带来的复杂的并发问题,JavaScript 只能设计成单线程的,这也是这门语言的核心特征之一。 JS 的宿主环境通过仅提供一个线程运行 JS 来保证 JS 代码的单线程运行。 单线程意味着 JS 引擎在同一时间只能做一件事情,即同步地执行代码。但在代码执行过程中不可避免地会遇到一些无法立即执行的任务,例如: 计时器到达时间后要执行的任务 — setInterval、setTimeout 网络请求完成后要执行的任务 — XMLHttpRequest、Fetch 监听到用户操作后要执行的任务 — addEventListener … … … 如果让执行 JS 的线程去处理这些任务,就会导致该线程处于长期阻塞的状态,而宿主环境中的 JS 执行线程往往还承担着极其重要的工作。例如,在浏览器中,如果执行 JS 的线程长期阻塞,就会导致浏览器卡死! 为避免上述情况发生,JS 的宿主环境使用了异步的方式来处理这种无法立即执行的任务。 当遇到异步任务时,宿主环境会将其交给其他线程处理,执行 JS 的线程则会立即结束当前任务转而去执行后续代码。 事件循环 事件循环是宿主环境处理 JS 异步操作,让其能够非阻塞式运行的机制。 不同宿主环境对事件循环的实现方式有所不同,不过在核心机制上大同小异。 接下来,本文将详细描述 JavaScript 在浏览器和 Node.js 这两个宿主环境中的事件循环机制。 浏览器事件循环 浅谈浏览器 聊浏览器的事件循环之前,我们先说说浏览器本身。 现代浏览器是一个多进程多线程的应用程序,内部工作极其复杂,其程度直逼操作系统。它拥有数个功能模块,为避免单个模块崩溃牵连其他模块,导致连锁反应,使浏览器彻底崩溃。浏览器在启动时,会开启多个进程,把不同的功能模块放在不同的进程里。 浏览器进程众多,其中比较重要的有: 📚 浏览器进程 浏览器进程是浏览器的主进程,无论打开多少浏览器窗口,它仅有一个。 它主要负责浏览器界面显示、用户交互和进程管理。 这里说的界面和交互,不是视窗内的网站界面。而是指浏览器本身自带的部分,如导航栏、书签栏、刷新按钮等。 刚打开浏览器的时候只有一个浏览器进程,其他进程都是它创建的。 📚 网络进程 网络进程主要负责处理网站的数据请求和响应,通常情况下,它与渲染进程的交互最为密切。每当网站需要进行资源请求,渲染进程就会将任务交给网络进程处理,网络进程取得响应结果后再返回给渲染进程。 网络进程内部会开启多个线程,以实现多网络请求的异步化处理。 📚 渲染进程 渲染进程负责控制和显示视窗部分(网站页面)的所有内容,主要是解析 HTML、CSS、JS 和其他资源,并生成渲染树、执行布局和绘制等操作。 ...