调用栈溢出详解
本文主要结合实例,介绍下调用栈溢出的问题,即对应解决方案
规则:Chrome/V8 引擎的调用栈上限约 10000 层,超过后直接抛出 Maximum call stack size exceeded 错误;代码终止执行,无法继续递归。
实例解释:
let stackCount = 0; // 记录递归层数
// 同步递归函数(无终止条件,会无限递归)
function recursiveFunc() {
stackCount++;
recursiveFunc(); // 函数最后一步又调用自己 → 同步递归
}
// 捕获栈溢出错误
try {
recursiveFunc();
} catch (error) {
console.log('递归层数:', stackCount); // Chrome中约 10000 层
console.log('错误信息:', error.message); // "Maximum call stack size exceeded"
}
控制台报错:
原因解释:
每次调用 recursiveFunc(),JS 引擎会在「调用栈」中创建一个「栈帧」(保存函数的执行状态);同步递归时,新的栈帧会不断叠加,直到调用栈的内存被占满;
如何规避
用「循环 + 变量」替代递归,调用栈始终只有 1 层
let loopCount = 0;
// 循环替代递归(调用栈始终只有1层)
function loopFunc() {
while (true) { // 无限循环(模拟无限递归)
loopCount++;
// 手动终止:避免死循环卡死,仅做演示
if (loopCount === 20000) {
console.log('迭代执行层数:', loopCount); // 20000层(无溢出)
break;
}
}
}
loopFunc(); // 执行正常,无错误
异步递归
let asyncCount = 0;
// 异步递归函数
async function asyncRecursiveFunc() {
asyncCount++;
// 手动终止:避免无限异步递归
if (asyncCount === 20000) {
console.log('异步递归层数:', asyncCount); // 20000层(无溢出)
return;
}
// 关键:setTimeout 清空当前栈,新递归在新栈中执行
await new Promise(resolve => setTimeout(resolve, 0));
await asyncRecursiveFunc();
}
asyncRecursiveFunc(); // 执行正常,无错误
setTimeout讲一个resolve塞入宏任务队列,await一个 asyncRecursiveFunc函数执行。此时,主线程栈空,拿出resolve,继续执行,完美屏蔽溢出问题。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
