深入理解 JavaScript 执行机制:事件循环、执行栈、同步与异步彻底搞懂
很多前端开发者在学习 JavaScript 时,都会遇到一个问题:
为什么 JavaScript 是单线程,却能处理异步任务?
为什么 Promise 比 setTimeout 先执行?
为什么代码执行顺序总是和写的顺序不一样?
要彻底理解这些问题,就必须搞清楚 JavaScript 的执行机制。
本文将系统讲清:
- 同步任务
- 异步任务
- 执行栈
- Web API
- 任务队列
- 宏任务与微任务
- 事件循环(Event Loop)
并通过执行流程一步一步拆解运行原理。
一、JavaScript 为什么需要执行机制?
JavaScript 是单线程语言。
也就是说:
同一时间只能执行一件事。
如果没有异步机制,浏览器将无法处理:
- 用户点击
- 网络请求
- 定时任务
- 页面渲染
整个页面会被阻塞。
因此浏览器设计了一套协作机制:
执行栈 + Web API + 任务队列 + 事件循环
让单线程也能处理异步。
二、同步任务与异步任务
1 同步任务
同步任务会立即执行,按顺序进入执行栈。
示例:
arduino
console.log(1)
console.log(2)
console.log(3)
执行顺序严格一致:
1 2 3
同步代码执行期间,后面的代码必须等待。
2 异步任务
异步任务不会立即执行,而是交给浏览器处理。
常见异步操作:
- setTimeout
- 网络请求
- DOM 事件
- Promise
当异步任务完成后,不会立刻执行,而是进入任务队列等待。
三、执行栈(Call Stack)
执行栈用于存放当前正在执行的函数。
执行规则:
后进先出(LIFO)。
函数调用时入栈,执行完成后出栈。
示例:
scss
function a(){ b() }
function b(){ c() }
function c(){ console.log("end") }
a()
执行顺序:
css
a → b → c → 执行 → 返回
四、Web API 的作用
JavaScript 本身没有定时器、DOM、Ajax。
这些都是浏览器提供的能力。
当执行:
scss
setTimeout(fn, 1000)
流程:
1 JS 交给浏览器 Web API
2 浏览器计时
3 时间到后,把回调函数放入任务队列
五、任务队列(Task Queue)
任务队列用于存放异步任务的回调函数。
执行条件:
只有执行栈清空,任务队列才会被取出执行。
六、宏任务与微任务(重点)
任务队列分两种。
宏任务
- setTimeout
- setInterval
- DOM事件
- Ajax
微任务
优先级更高:
- Promise.then
- MutationObserver
- queueMicrotask
执行顺序:
同步代码
↓
执行所有微任务
↓
执行一个宏任务
↓
再执行微任务
↓
循环
七、事件循环(Event Loop)
事件循环是 JavaScript 的调度中心。
工作逻辑:
不断检查执行栈。
如果执行栈为空:
1 先执行微任务队列
2 再执行宏任务队列
不断重复。
八、完整执行流程示例
javascript
console.log(1)
setTimeout(()=>{
console.log(2)
})
Promise.resolve().then(()=>{
console.log(3)
})
console.log(4)
执行顺序:
1 同步执行 → 1 4
2 执行微任务 → 3
3 执行宏任务 → 2
最终输出:
1 4 3 2
九、执行机制整体流程总结
JavaScript 运行流程:
1 执行同步代码进入执行栈
2 遇到异步任务交给 Web API
3 异步完成进入任务队列
4 执行栈清空后
5 事件循环调度任务执行
6 无限循环
十、一句话总结
JavaScript 通过:
执行栈 + Web API + 任务队列 + 事件循环
实现了单线程异步执行模型。
理解事件循环,本质就是理解:
代码什么时候进入执行栈。
如果这篇文章对你有帮助,欢迎点赞收藏。