1. ホーム
  2. node.js

[解決済み] Node.js - 最大コールスタックサイズを超えました。

2022-02-07 21:59:34

質問

私のコードを実行すると、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
    ...
});

このループは、1ラウンドごとに少し時間(ブラウザの1ラウンドトリップ)を失うので、遅すぎるかもしれません。しかし 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
    ...
});