繁体   English   中英

如何捕获堆栈溢出错误

[英]How to trap stack overflow error

我有一个带异步接口的递归函数,在调用时可能超过堆栈深度限制:

function f(x, cb) {
    if (x === 0) {
        cb();
    } else {
        f(x - 1, cb);
    }
}

f(1e6, function() {
    console.log('done');
});  // BOOM

(是的,它必须是递归的,重写它是迭代的是不可行的)。

我可以通过异步执行递归调用来解决这个问题(例如通过setTimeoutwindow.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严格模式。 我更喜欢便携式解决方案,但实际上只需要一个用于镀铬。

不幸的是,浏览器没有暴露(JS)方法来检查调用堆栈大小,就像你说的getCurrentStackDepth 我试图找出它并且无法获得与之相关的任何信息。

最后一个解决方案可以是解决此问题的方法之一。 JS引擎为堆栈大小超出的异常对象的类型是RangeError 因此,使用该信息和异常的消息信息,我们可以编写isStackOverflowError方法,如下所示

function isStackOverflowError(e) {
    return (e instanceof RangeError) && /.+stack.+size.+/.test(e.message);
}

此外,与示例2相比,这具有更好的性能,但仍然不会太快。 如果我找到更好的方法来解决这个问题,我会尝试更新。

这是一种方法:

function f(x, cb) {
    if (x === 0) {
        cb();
    } else {
      try {
        f(x - 1, cb);
      } catch (e) {
        setTimeout( function() { f(x-1, cb); }, 0 );
      }
    }
}

f(100000,function(){console.log("Done!")})

但是,堆栈溢出是一个编程错误,我不能真的建议这样解决它。 将递归代码重构为迭代更好,因为它们在功能上是等价的。 迭代版本将使用从堆分配的“堆栈”,其可以远远大于典型的进程堆栈。

有关更多信息,请参阅递归与迭代

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM