当我运行代码时,Node.js抛出了一个由太多递归调用引起的"RangeError: Maximum call stack size exceeded"
异常.我试图增加 node .js堆栈大小为sudo node --stack-size=16000 app
,但 node 数为.js崩溃时没有任何错误消息.当我在没有sudo的情况下再次运行时, node .js打印'Segmentation fault: 11'
.有没有可能在不删除递归调用的情况下解决这个问题?
当我运行代码时,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是清除堆栈的机会.如果不这样做,并且有许多循环没有任何real异步函数调用,或者如果不等待回调,那么RangeError: Maximum call stack size exceeded
将是inevitable.
有很多关于"潜在异步循环"的文章.Here is one
下面是一些示例代码:
// 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
分.通常每setTimeout
0次就可以了.但这可能因堆栈大小而异:
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
...
});