调用栈和任务队列 Event Loop 执行顺序
调用栈
- 定义
- 核心特性
- 执行同步代码
- 栈溢出
- 无限递归同步代码,会导致栈溢出错误(Stack Overflow)
- 阻塞主线程
- 调用栈中的任务执行时,主线程会被阻塞,无法处理其他任务
任务队列
- 定义
- 异步任务管理器
- 分类
- 宏任务
- setTimerout / setInerval / setImmediate / requestAnimationFrame / IO操作 / UI渲染 / DOM事件回调 等
- 微任务
- Promise / async/await / MutationObserver / process.nextTick / queueMicrotask() 等
- 核心特性
- 非阻塞
- 异步任务回调函数不直接进入调用栈,而是被推入任务队列,由Event Loop调度到调用栈中再执行
- 按优先级处理
- 微任务优先宏任务
- 每次调用栈清空后,会先清空所有微任务,再处理下一个宏任务
- 浏览器渲染时机
- 通常在微任务清空后 / 宏任务执行前,浏览器kennel进行渲染
Event Loop
- 概念
- 事件循环,是一种计算机程序架构,用于等待和发送消息及事件。
- 产生的背景
- JS是单线程语言,同时只能执行一个人任务,如果所有任务都是同步执行,会导致主线程阻塞和页面卡死;
- 作用
- 负责协调不同类型任务的执行,解决了单线程环境下异步操作的处理问题,避免主线程阻塞,保持页面响应性
- 意义
- 避免主线程阻塞,保持页面响应性
- 高效处理高并发
- 精准控制执行顺序
Event Loop 执行顺序
- 执行调用栈中的同步代码
- 清空微任务队列
- 浏览器可能渲染页面(重排/重绘)
- 从宏任务队列中取出一个任务,推入调用栈执行(每次Event Loop只执行一个宏任务)
- 重复1~4步骤形成事件循环
宏任务:
- 定义
- 由宿主环境(如浏览器或 Node.js)发起的异步任务
- 特点
- 消息队列中的等待被主线程执行的事件,宏任务执行时都会重新创建执行栈
微任务:
- 定义
- 由 JavaScript 引擎发起的异步任务,通常与 Promise 相关
- 是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前