我有一个异步接口的递归函数调用时,将有可能超过堆栈深度限制:如何捕获堆栈溢出错误
function f(x, cb) {
if (x === 0) {
cb();
} else {
f(x - 1, cb);
}
}
f(1e6, function() {
console.log('done');
}); // BOOM
(是的,它必须是递归的,重写它是反复被不可行)。
我可以通过异步操作的方式递归调用(例如,经由setTimeout
或window.postMessage
,这是假想更快)解决此问题:
function f(x, cb) {
if (x === 0) {
cb();
} else {
setTimeout(function() {
f(x - 1, cb);
}, 0);
}
}
f(1e6, function() {
console.log('done');
}); // ok
但是,这是显著慢。所以我只想在异常调用会导致堆栈溢出时才进行异步调用。喜欢的东西
function f(x, cb) {
if (x === 0) {
cb();
} else {
if (getCurrentStackDepth() == getMaxStackDepth() - 42)
setTimeout(function() {
f(x - 1, cb);
}, 0);
} else {
f(x - 1, cb);
}
}
}
,或者,如果这是不可能的,当一个溢出发生至少检测和异步重试。东西沿线
function f(x, cb) {
if (x === 0) {
cb();
} else {
try {
f(x - 1, cb);
} catch (e) {
if (isStackOverflowError(e)) {
setTimeout(function() {
f(x - 1, cb);
}, 0);
} else {
throw e;
}
}
}
}
这是怎么做到的?通过Function.prototype.caller
的解决方案是不可接受的,因为我处于es5-es6严格模式。我更喜欢便携式解决方案,但真的只需要一个铬。
将递归重写为循环要容易得多。是的,你说它必须是递归的,但是当你使用'setTimeout'时,它不会再递归了。 – Kenney
@Kenney这是一个例子,我实际上有大约十几个相互递归的异步分支函数。 –
好的。使用'setTimeout'来避免堆栈溢出是一个不好的决定,恕我直言。你原来的函数基本归结为'for(; x> 0; x--){}; CB();'。 (顺便说一下,你确实意识到你的顶级函数不是异步的?) – Kenney