Web Worker 与 Node 的 worker_threads:别把「多线程」想成多开几个 setTimeout
知识背景
浏览器主线程要处理 JS、样式、布局、很多用户输入;长时间算斐波那契或大图处理会卡死页面。
Web Worker 让浏览器在后台线程跑脚本,通过 postMessage 与主线程通信。
Node 虽是服务端,但默认执行用户 JS 也在主线程里;遇到 CPU 重活同样会拖住事件循环。worker_threads 模块提供真正的操作系统线程跑 JS(带隔离的上下文),用于 offload 计算。
二者都解决「别让唯一的主线程扛所有 CPU 重活」,但内存模型、API、使用约束不同,容易混谈。
知识详解与通俗解释
1. 浏览器 Web Worker:能做什么、不能做什么
能:纯计算、编解码、处理缓冲区等;通过结构化克隆算法传递数据(多数对象可拷贝,函数 DOM 节点不能直接共享)。
不能:直接访问 window、document、DOM;需要 UI 更新时,把结果 postMessage 回主线程再改 DOM。
通俗说:Worker 像后厨切菜,切完把菜递到前台(主线程)装盘,顾客(用户)不会堵在柜台看你切一小时。
SharedWorker / Service Worker 是另外两类「工人」,场景不同;面试里常说的「Web Worker」多指 Dedicated Worker。
2. Worker 与主线程怎么传数据?
postMessage:默认拷贝数据(大数组有成本)。Transferable:如ArrayBuffer,可转移所有权(发送方失去引用),避免大块内存复制。
通俗说:要么「复印一份寄过去」,要么「把原件快递过去,自己手里这份作废」。
3. Node 的 worker_threads:为什么需要?
Node 适合 I/O 密集(见前文事件循环);若在主线程做 CPU 密集,会阻塞定时器、网络回调,表现为延迟飙升。worker_threads 可新建 Worker,跑独立入口文件或内联代码,通过 MessageChannel / parentPort 通信,并可用 SharedArrayBuffer + Atomics 做更低级共享(需注意安全与平台支持)。
通俗说:主线程像餐厅前台接单;worker_threads 是再雇几个厨师并行炒菜,前台继续接电话。
4. 和「子进程 child_process」怎么选(极简)
| 方式 | 特点 |
|---|---|
| worker_threads | 同进程多线程,启动相对轻,共享同一进程的部分资源,适合并行算力 |
| child_process | 新进程,隔离更强,崩溃不拖死父进程,适合跑别的可执行文件 / 沙箱边界 |
5. 常见误区
- Worker 不是万能的:创建与通信有成本,小任务反而更慢。
- 不是「多线程就快了」:还要考虑数据拆分、锁、负载;前端大数组传递要考虑拷贝。
- Node 里仍要以异步 I/O 为主;Worker 解决的是 CPU,不是替代
fs.readFile那种等待。
总结
- Web Worker:浏览器里脱离主线程跑 JS,不碰 DOM,用消息传结果。
worker_threads:Node 里并行 CPU 任务,减轻事件循环阻塞。- 两者都是 「主线程负责调度与 I/O 响应,重算挪出去」;选型时要分清瓶颈是 等磁盘/网络 还是 算不过来。
- 与「宏任务/微任务」文章连读:事件循环管异步排队;Worker 管并行算力——不是一回事。