当我运行我的代码时,Node.js引发"RangeError: Maximum call stack size exceeded"由过多的递归调用引起的异常。我试图将Node.js堆栈大小增加sudo node --stack-size=16000 app,但是Node.js崩溃而没有任何错误消息。当我不使用sudo再次运行此命令时,Node.js将输出'Segmentation fault: 11'。是否有可能在不删除递归调用的情况下解决此问题?
"RangeError: Maximum call stack size exceeded"
sudo node --stack-size=16000 app
'Segmentation fault: 11'
您应该将递归函数调用包装到
setTimeout
setImmediate
process.nextTick
函数使node.js有机会清除堆栈。如果您不这样做,并且有很多循环没有任何 真正的 异步函数调用,或者如果您不等待回调,那么您RangeError: Maximum call stack size exceeded将 不可避免 。
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每次都跟注。通常,每千次可以这样做。但这可能会有所不同,具体取决于您的堆栈大小:
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 ... });