JS事件循环详解

为什么JavaScript是单线程

JavaScript语言的一大特点就是单线程,即同一时间内,JavaScript只能做一件事。

其实,JavaScript的单线程特点和它的用途是有关系的,试想,如果JavaScript是多线程的,此时有两个线程,一个线程修改某个DOM元素的内容,另一个线程又去删除这个DOM,那么这时浏览器就不知道该怎么办了,造成不必要的混淆。

所以,为了避免复杂,JavaScript做成单线程语言,这样就很清晰了。

任务队列

因为JavaScript的单线程机制,这就意味着所有的任务都必须排队执行,前一个任务执行完成,后一个任务才会执行,如果此时队列中有一个非常耗时的操作,后一个任务就不得不持续等待。

但是JavaScript设计者不会允许这样的事情发生,所以JavaScript会将异步事件挂在与当前执行栈不同的队列中,这个队列我们称之为任务队列

JavaScript主线程执行时,会从执行栈顶取出一个任务执行,执行完后再取任务执行,周而复始。

任务分类

在JavaScript中,我们将全部的任务分为两种:同步任务和异步任务。

异步任务又分为宏任务微任务

  • 宏任务(macrotask):一些异步任务的回调函数会依次进入宏任务队列(macro task queue),这些异步任务包括:
    • setTimeout
    • setInterval
    • setImmediate (Node独有)
    • requestAnimationFrame (浏览器独有)
    • I/O
    • UI rendering (浏览器独有)
  • 微任务(microtask):另一些异步任务的回调函数会依次进入宏任务队列(micro task queue),包括:
    • process.nextTick (Node独有)
    • Promise.then
    • Object.observe
    • MutationObserver

Event Loop

先来看一张图:

这张图将浏览器的event loop完整的流程描述了出来,具体如下:

  1. 执行全局Script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);
  2. 全局Script代码执行完毕后,调用栈Stack会清空;
  3. 从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
  4. 继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;
  5. microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;
  6. 取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;
  7. 执行完毕后,调用栈Stack为空;
  8. 重复第3-7的步骤;

面试题详解

题目:

javascript 复制代码
setTimeout(function () {
    console.log(1);
});
new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})
console.log(4);

分析下代码的执行过程:

  1. 执行script
  2. 第一行遇到setTimeout,宏任务,放到宏任务队列中,继续执行
  3. 第4行遇到promisepromise里面的任务是同步任务,是立即执行的,所以打印2,并执行resolve
  4. 第七行,遇到then,微任务,放到微任务队列中,继续执行
  5. 第十行,遇到打印语句,同步任务,直接执行打印4,
  6. 此时,执行栈空了,JavaScript开始从微任务队列中依次取出微任务压入执行栈中执行,打印3
  7. 微任务队列空了,JavaScript开始从宏任务队列中依次取出宏任务压入执行栈中执行,打印1. 所以最终结果是2,4,3,1
相关推荐
Hilaku13 小时前
为什么大厂对前端算法要求极高?
前端·javascript·程序员
你可如意13 小时前
从Promise开始,一篇让你彻底建立起JS的异步编程模型
javascript
用户842981424181014 小时前
AI时代,JS混淆加密还有用吗?
前端·javascript
申君健248641827720215 小时前
MCP TypeScript SDK v2 完整升级变化说明
javascript
Ivanqhz15 小时前
Rust Lazy浅析
java·javascript·rust
铁皮饭盒16 小时前
面试官:如何用 Bun + JS 实现安全的文件 MCP 工具集
前端·javascript·后端
weixin_BYSJ198718 小时前
springboot3家政平台小程序--附源码00904
java·javascript·spring boot·python·django·flask·php
Wect19 小时前
TypeScript 完全指南
前端·javascript·typescript
张元清19 小时前
React useDeepCompareEffect:修复 useEffect 的对象依赖问题(2026)
javascript·react.js
phltxy20 小时前
LangChain_v1_Agent快速开发和更新说明
前端·javascript·langchain