🔥从进程线程聊到JS执行机制,这次彻底搞懂事件循环!(上)

前言:从"单线程"到"异步"

你是否曾在面试中被问到:"说说 JavaScript 的事件循环(Event Loop) ?"

是否曾在实际开发中遇到过这样的问题:为什么 Promise.then 会比 setTimeout 先执行?为什么 setTimeout(fn,0) 并不是"立刻执行"?

甚至,你是否曾经疑惑:JavaScript 是单线程的,那它是如何做到"看起来很聪明",处理各种异步操作的?

要真正理解这些问题,我们需要跳出 JavaScript 本身,把目光投向浏览器。JavaScript 的单线程模型,是它最独特的设计之一,而事件循环,正是这套机制背后的核心驱动力。

但事件循环并不是"孤立运行"的。它背后依赖于浏览器的多进程架构多线程协作,以及一套精妙的任务调度机制。JavaScript 主线程负责执行代码,定时器线程负责计时,网络线程负责请求,渲染线程负责页面更新。它们各司其职,又通过事件循环紧密配合。

在这两篇文章中,我们将深入讲解:

  • 进程与线程的基本概念 讲起,
  • 深入 浏览器的多线程架构
  • 揭开 事件循环的执行流程
  • 并结合实际代码剖析 宏任务与微任务的区别 , 最终让你对 JavaScript 的异步执行机制有一个 系统、清晰、可落地 的理解。

无论你是想深入前端底层原理,还是准备迎接下一场技术面试,这篇文章都将为你打下坚实的基础。

进程&线程

进程(Process)

  • 定义:进程是程序的一次运行实例,是操作系统进行资源分配和调度的最小单位。

  • 特点

    • 每个进程都有自己的 独立内存空间(包括代码段、堆、栈等)。
    • 进程之间是 相互隔离 的。
    • 一个程序可以有多个进程(如浏览器打开多个标签页)。
  • 开销

    • 创建和销毁进程开销大,进程之间的切换也需要花费较多资源。

举个例子:如果我们现在桌面上打开了浏览器网易云音乐 ,那么操作系统会为每个程序创建一个进程(Process),浏览器是一个进程(比如 Chrome 的每个标签页可能是一个独立渲染进程),网易云音乐是另一个进程。

这两个进程彼此隔离,互不干扰。即使浏览器崩溃了,网易云音乐也不会因此崩溃。

单进程模型&多进程模型

而我们的应用程序(APP) ,又分为单进程模型多进程模型

单进程模型中,所有功能都在一个进程中执行,例如早期的浏览器(IE6),或者老版本的笔记本,都是单进程,它们的实现比较简单,不需要进行跨进程通信,但是如果一个模块出了错(比如某个插件加载失败),整个进程都会崩溃。

多进程模型 最好的例子就是我们现在用的浏览器了,比如Chrome浏览器,其采用多进程架构,包括:

  • 浏览器主进程(Browser Process) :负责管理窗口、标签页、安全策略等。
  • 渲染进程(Renderer Process) :每个标签页可能是一个独立的渲染进程,负责解析 HTML、执行 JS、渲染页面。
  • GPU 进程(GPU Process) :负责图形渲染。
  • 网络进程(Network Process) :处理所有网络请求。
  • 插件进程(Plugin Process) :运行第三方插件(如 Flash)。

多进程模型有很多优点,比如它的隔离性比较好 ,一个标签页崩溃不会影响整个浏览器的运行,它的稳定性比较强,关键功能都是独立进程处理,它还能用多核CPU并行处理任务。

至于它的缺点,就是每个进程都有独立的内存,占用空间较大,再者就是进程间通讯比较复杂

线程(Thread)

  • 定义:线程是 CPU 调度的最小单位,一个进程可以包含多个线程。

  • 特点

    • 同一进程下的线程共享该进程的资源(如内存、变量等)。
    • 线程之间的切换比进程快得多。
    • 线程之间容易通信和共享数据。
  • 注意

    • 多线程并发执行时,要注意线程安全问题(比如资源竞争)。

以浏览器为例,它内部可能有:

  • JS 主线程:执行 JavaScript 代码
  • 渲染线程:负责页面绘制
  • 网络线程:处理 HTTP 请求
  • 定时器线程:管理 setTimeout
  • 合成线程:负责页面合成和渲染优化

而网易云音乐也可能有:

  • 音频播放线程:播放音乐
  • UI 线程:更新界面
  • 网络线程:加载歌词、封面、流媒体数据

这些线程都在各自的进程中运行,协同工作。

线程之间的协作:并发 vs 并行

你的 CPU 可能只有一个或多个物理核心,但你同时在听歌、看网页、甚至在浏览器中还有多个标签页在运行。

这是如何做到的?

这就涉及到调度器(Scheduler) 的工作:

  • 并发(Concurrency) :多个任务交替执行(不是真正同时)
  • 并行(Parallelism) :多个任务同时执行(需要多个 CPU 核心)

在只有一个 CPU 核心的场景下,操作系统会通过时间片轮转的方式,让多个线程轮流执行,从而实现"并发"。

比如:

  • 浏览器的 JS 主线程执行一段代码
  • 时间片到,操作系统调度器切换到网易云音乐的音频线程
  • 之后又切换回浏览器的网络线程,继续加载资源

你感觉它们在"同时运行",其实只是切换得非常快(通常每几毫秒切换一次)。

浏览器的多线程架构

浏览器是多进程的,多线程的 ,就拿浏览器的渲染进程来说,我们每打开一个页面就有一个新的渲染进程产生,渲染进程中主要有下面几个线程:

线程名称 功能说明
主线程(Main/UI Thread) 执行 JavaScript、解析 HTML/CSS、处理用户交互(点击、滚动等)
V8 引擎线程(JS Engine Thread) 执行 JavaScript 代码,管理 JS 的堆栈和垃圾回收
渲染线程(Rendering Thread) 负责将 DOM + CSSOM 合成 Render Tree、布局(Layout)、绘制(Paint)
合成线程(Compositor Thread) 合成页面中的各个图层,准备最终显示画面
光栅化线程(Raster Thread) 将页面内容转换为像素,准备显示
IO 线程(IO Thread) 处理与其它进程(如网络进程)的通信,加载资源
定时器线程(Timer Thread) 管理 setTimeoutsetInterval 等定时任务
异步任务线程(Async Task Thread) 处理异步请求(如 fetchXMLHttpRequest
Web Worker 线程(Worker Threads) 在后台执行 JavaScript,不会阻塞主线程

线程的配合(打开新网页为例)

那么线程之间是如何配合的呢?我们就以新开一个网页为例子吧!

当你打开一个网页时,浏览器的线程们是这样协作的:

IO 线程网络线程获取 HTML 数据

主线程解析 HTML,构建 DOM 树

遇到 <script> 标签,暂停解析,交给 V8 引擎线程执行 JS

执行完 JS 后,继续解析 HTML/CSS,生成 CSSOM

构建 Render Tree,进行 Layout(布局)

交给 光栅化线程生成像素图像

合成线程将各图层组合成最终画面并显示

用户交互(如点击按钮)触发事件处理,回到主线程执行

看到这里大家应该对于进程线程有了基本的理解了,从浏览器的多进程多线程设计,我们透过门的一个小缝,瞥见了JavaScript这门语言的设计,开始理解了它为什么是一门单线程语言 ,下面我们将扒了他的朝服来看看究竟怎么个事

JavaScript?单线程?

JavaScript 引擎(如 V8、SpiderMonkey)中,同一时间只能执行一段代码 ,不能并行执行多个 JavaScript 任务。

JavaScript 诞生于 1995 年,最初是为了在网页中添加一些简单的交互(如表单验证、动画等)。当时的设计目标是:

  • 简单易用:开发者不需要处理复杂的多线程问题(如死锁、竞态条件等)
  • 快速实现:浏览器实现更简单,运行效率更高
  • 避免资源争用:如果多个线程同时操作 DOM,会导致状态不一致

如果 JavaScript是一个多线程语言的话,它将面对一些问题:

假设有两个线程同时操作一个 DOM 元素:

  • 线程 A:修改元素颜色为红色
  • 线程 B:修改元素颜色为蓝色

这时候浏览器不知道该优先执行哪个操作,会引发同步问题(race condition)。

所以为了避免这些复杂性,它的设计者把它设计成了一门单线程语言,毕竟这门语言只用了仅仅一周的时间就设计出来 了,最开始做的也不是很复杂的工作,所以就以简为优

而单线程有一个不好的地方就是不能实现异步 ,但是JavaScript借助浏览器的其他线程和node.js的某些能力是可以实现异步编程的,其和事件循环 相结合,做到了异步非阻塞编程

而事件循环就是我们下一期需要讲的了......

总结

这一期我们学习了进程和线程的有关概念,知道了进程包含多个线程,而一个APP可以包含多个进程,这些进程与线程相互合作配合,最终共同达到我们想要计算机实现的效果。随后我们介绍了浏览器的多线程架构与JS单线程的设计理念,下一期我们将走进事件循环

相关推荐
上海大哥13 分钟前
Flutter 实现工程组件化(Windows电脑操作流程)
前端·flutter
刘语熙21 分钟前
vue3使用useVmode简化组件通信
前端·vue.js
XboxYan1 小时前
借助CSS实现一个花里胡哨的点赞粒子动效
前端·css
码侯烧酒1 小时前
前端视角下关于 WebSocket 的简单理解
前端·websocket·网络协议
OEC小胖胖2 小时前
第七章:数据持久化 —— `chrome.storage` 的记忆魔法
前端·chrome·浏览器·web·扩展
OEC小胖胖2 小时前
第六章:玩转浏览器 —— `chrome.tabs` API 精讲与实战
前端·chrome·浏览器·web·扩展
不老刘2 小时前
基于clodop和Chrome原生打印的标签实现方法与性能对比
前端·chrome·claude·标签打印·clodop
ALLSectorSorft2 小时前
定制客车系统票务管理系统功能设计
linux·服务器·前端·数据库·apache
xiaopengbc2 小时前
B站,视频号怎么下载?,猫抓cat-catch离线版下载,Chrome扩展插件
前端·chrome
ZzMemory2 小时前
深入理解JS(九):IIFE,即执函数的锁域魔法
前端·javascript·面试