
JavaScript 是单线程语言,靠事件循环(Event Loop)协调同步与 异步任务,实现非阻塞运行。
JavaScript 如何运行:从调用 栈开始
代码执行时,函数调用被压入 ** 调用栈(Call Stack)**,遵循后进先出原则。同步代码立即执行,栈空则当前任务完成。
例如:console.log(“A”); setTimeout(() => console.log(“B”), 0); console.log(“C”); 会先输出 A、C,B 稍后才出现——因为 setTimeout 的回调不进调用栈,而是交给 Web API 暂存。
事件循环如何调度异步任务
事件循环持续监听两个关键队列:
立即学习“Java 免费学习笔记(深入)”;
- 宏任务队列(Macrotask Queue):包含 setTimeout、setInterval、I/O、UI 渲染 等任务
- 微任务队列(Microtask Queue):包含 Promise.then/catch/finally、queueMicrotask()、MutationObserver
每次宏任务执行完,事件循环会 清空整个微任务队列,再取下一个宏任务——这就是 Promise 回调总比 setTimeout 先执行的原因。
一个典型执行顺序示例
运行以下代码:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
输出为:1 → 4 → 3 → 2。逻辑是:
- 1 和 4 是同步代码,进调用栈立即执行
- setTimeout 回调进入宏任务队列
- Promise.then 进入微任务队列
- 同步代码执行完毕,调用栈为空 → 事件循环处理微任务(输出 3)→ 再取宏任务(输出 2)
注意:Node.js 与 浏览器 略有差异
Node.js 的事件循环有更细的阶段划分(如 timers、pending callbacks、poll、check、close callbacks),process.nextTick() 的优先级甚至高于 Promise 微任务;而浏览器中 Promise 始终是最高等级微任务。实际开发中,应以 微任务 这一基本原则为准,避免跨环境强依赖执行时序。
以上就是