你应该把你的递归函数调用包装成一个
-
setTimeout,
-
setImmediate 或
-
process.nextTick
函数让 node.js 有机会清除堆栈。如果您不这样做并且有许多没有任何真正异步函数调用的循环,或者如果您不等待回调,您的RangeError: Maximum call stack size exceeded 将不可避免。
有很多关于“潜在异步循环”的文章。 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。正常情况下是可以的。每 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
...
});