一尘不染

Node.js-超出最大调用堆栈大小

node.js

当我运行我的代码时,Node.js引发"RangeError: Maximum call stack size exceeded"由过多的递归调用引起的异常。我试图将Node.js堆栈大小增加sudo node --stack-size=16000 app,但是Node.js崩溃而没有任何错误消息。当我不使用sudo再次运行此命令时,Node.js将输出'Segmentation fault: 11'。是否有可能在不删除递归调用的情况下解决此问题?


阅读 253

收藏
2020-07-07

共1个答案

一尘不染

您应该将递归函数调用包装到

  • 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每次都跟注。通常,每千次可以这样做。但这可能会有所不同,具体取决于您的堆栈大小:

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
    ...
});
2020-07-07