Node.js如何超出最大调用堆栈大小?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (192)

当我运行我的代码时,Node.js抛出"RangeError: Maximum call stack size exceeded"过多的递归调用引起的异常。我试图通过增加Node.js堆栈大小sudo node --stack-size=16000 app,但Node.js崩溃没有任何错误消息。当我没有sudo再次运行这个,然后Node.js打印'Segmentation fault: 11'。有没有可能解决这个问题,而不删除递归调用?

提问于
用户回答回答于

应该将递归函数调用包装为一个

  • setTimeout,
  • setImmediate 要么
  • process.nextTick

函数为node.js提供清除堆栈的机会。如果你不这样做,并且没有任何真正的异步函数调用有很多循环,或者如果你不等待回调,你的RangeError: Maximum call stack size exceeded将是不可避免的

有许多关于“潜在的异步循环”的文章。

现在再来看一些示例代码:

// ANTI-PATTERN
// THIS WILL CRASH

var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) { 
            someAsyncFunc( function( err, result ) { 
                potAsyncLoop( i+1, callback );
            });
        } else {
            // this will crash after some rounds with
            // "stack exceed", because control is never given back
            // to the browser 
            // -> no GC and browser "dead" ... "VERY BAD"
            potAsyncLoop( i+1, resume ); 
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});

这是对的:

var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) { 
            someAsyncFunc( function( err, result ) { 
                potAsyncLoop( i+1, callback );
            });
        } else {
            // Now the browser gets the chance to clear the stack
            // after every round by getting the control back.
            // Afterwards the loop continues
            setTimeout( function() {
                potAsyncLoop( i+1, resume ); 
            }, 0 );
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});

现在循环可能会变得太慢,因为我们每轮都会放松一点时间(一次浏览器往返)。但是你不必setTimeout每一次都调用一次。通常每1000次就可以做一次。但是这可能会因堆栈大小而有所不同:

var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) { 
            someAsyncFunc( function( err, result ) { 
                potAsyncLoop( i+1, callback );
            });
        } else {
            if( i % 1000 === 0 ) {
                setTimeout( function() {
                    potAsyncLoop( i+1, resume ); 
                }, 0 );
            } else {
                potAsyncLoop( i+1, resume ); 
            }
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});
用户回答回答于
/bin/bash -c "ulimit -s 65500; exec /usr/local/bin/node --stack-size=65500 /path/to/app.js"

但它只是增加了调用堆栈限制。不适合生产代码,但我只需要它运行一次的脚本。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励