You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript中的Event Loop(事件循环)是什么?如何理解call stack的管理?

JavaScript调用栈与事件循环核心解析

一、什么是调用栈?

调用栈是JS引擎用来追踪函数执行顺序的栈结构,完全遵循「后进先出(LIFO)」的规则——最后被压入栈的函数,会最先被执行并弹出栈。

简单来说,每次你调用一个函数,JS就把这个函数“压”进栈里;函数执行完,就把它“弹”出去。栈里的任务必须一个个执行完,不会中途插队。

举个直观的代码例子:

function greet(name) {
  console.log(`Hello, ${name}`);
}
function sayHi() {
  greet('Alice');
}
sayHi();

执行流程拆解:

  1. 执行sayHi(),把sayHi压入调用栈
  2. sayHi内部调用greet('Alice'),把greet压入栈(此时栈顶是greet)
  3. greet执行console.log,内置函数console.log被压入栈,执行后弹出
  4. greet执行完毕,弹出栈
  5. sayHi执行完毕,弹出栈,调用栈清空

调用栈的“阻塞”问题

因为JS是单线程,调用栈里的任务必须同步执行完才能处理下一个。如果遇到耗时的同步操作(比如百万级别的循环、复杂的计算),调用栈会一直被占用,主线程就会被“卡住”——浏览器没法响应用户点击、页面渲染也会暂停,直到栈里的任务执行完。

二、事件循环怎么和调用栈配合工作?

为了解决主线程阻塞的问题,JS引入了任务队列和事件循环机制,核心逻辑就是:把异步任务的回调放到队列里,等调用栈空了再处理。

具体运作步骤:

  1. JS引擎先执行调用栈里的所有同步任务,直到调用栈完全清空
  2. 事件循环开始工作,检查任务队列里有没有等待执行的回调函数
  3. 如果有,就把第一个回调函数压入调用栈执行;执行完后,再次检查调用栈是否为空,重复这个循环
  4. 如果队列里没有任务,事件循环就会进入等待状态,直到有新的异步任务加入队列

举个异步代码的例子:

console.log('开始');
setTimeout(() => {
  console.log('定时器回调');
}, 0);
console.log('结束');

执行流程拆解:

  1. console.log('开始')压入栈,执行后弹出,输出「开始」
  2. 遇到setTimeout,JS引擎不会等待倒计时,而是直接把它的回调函数放到任务队列里,继续执行后续同步代码
  3. console.log('结束')压入栈,执行后弹出,输出「结束」
  4. 调用栈清空,事件循环检测到任务队列里有回调,把它压入栈执行,输出「定时器回调」

补充:调用栈的细节注意点

  • 每个被压入栈的函数,都会创建自己的执行上下文(包含变量、作用域链等信息),函数弹出栈时,对应的执行上下文也会被销毁
  • 如果函数递归调用没有终止条件,会导致调用栈不断被压入新的函数,超过引擎允许的最大栈深度,抛出Stack Overflow错误,比如:
function infiniteLoop() {
  infiniteLoop();
}
infiniteLoop(); // Uncaught RangeError: Maximum call stack size exceeded

内容的提问来源于stack exchange,提问作者Snoopy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:03:24