JavaScript 是单线程语言,因其浏览器主线程仅有一个调用栈,为避免 DOM 竞态问题而刻意设计;异步任务由宿主环境多线程处理,JS 主线程依事件循环执行宏任务与微任务。

JavaScript 是单线程语言,因为它在浏览器主线程中只有一个调用栈,同一时间只能执行一个同步任务。这不是技术缺陷,而是有意为之的设计选择——为避免 DOM 操作的竞态问题,比如两个线程同时删节点又加样式,浏览器根本没法决定以谁为准。
为什么 DOM 操作强制要求单线程
浏览器渲染线程和 JS 主线程是互斥的:JS 正在执行时,页面暂停渲染;渲染进行时,JS 被挂起。如果 JS 支持多线程,就可能出现:
- 线程 A 执行
document.getElementById('app').innerHTML = 'loading...' - 线程 B 同时执行
document.body.removeChild(app) - 结果不可预测:节点可能被删了又赋值,或赋值后立刻报错“Cannot set property innerHTML of null”
所以,单线程不是妥协,是保障 DOM 操作可预测、可调试的底线。
异步不是 JS 引擎干的,是浏览器/Node.js 帮你扛的
真正处理 setTimeout、fetch、addEventListener 的,不是 JS 引擎本身,而是宿主环境提供的多线程能力:
立即学习“Java免费学习笔记(深入)”;
-
setTimeout由定时器线程计时,到期后把回调推入宏任务队列 -
fetch由网络线程发起请求,响应后把then回调推入微任务队列 -
click事件由 DOM 事件线程监听,触发后把事件处理器排队
JS 主线程只做一件事:清空调用栈 → 执行一个宏任务 → 清空所有微任务 → 再取下一个宏任务。这就是事件循环(Event Loop)的真实节奏。
对于一个刚进入PHP 开发大门的程序员,最需要的就是一本实用的开发参考书,而不仅仅是各种快速入门的only hello wold。在开发的时候,也要注意到许多技巧和一些“潜规则”。PHP是一门很简单的脚本语言,但是用好它,也要下功夫的。同时,由于PHP 的特性,我一再强调,最NB 的PHP 程序员都不是搞PHP 的。为什么呢?因为PHP 作为一种胶水语言,用于粘合后端 数据库和前端页面,更多需
微任务 vs 宏任务:最容易写错的执行顺序
下面这段代码输出顺序是 1 → 4 → 3 → 2,很多人误以为 setTimeout 是“0毫秒立即执行”:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
原因在于:
-
console.log(1)和console.log(4)是同步任务,立刻进栈执行 -
Promise.then是微任务,排在当前宏任务(整个脚本)末尾、下一个宏任务之前 -
setTimeout是宏任务,必须等当前宏任务+所有微任务执行完,才进入下一轮事件循环
这个优先级规则在 Vue、React 的更新时机、await 后续逻辑、甚至 MutationObserver 中都直接影响状态一致性。
真正难的不是记住“单线程”,而是理解:当 while (Date.now() 这种长任务卡住主线程时,不仅 UI 冻结,连 requestIdleCallback、resize 事件、甚至 Promise 微任务都会被延后——因为事件循环根本没机会跑起来。这种“假死”不是异步失效,恰恰是单线程最诚实的表现。










