web-dev-qa-db-ja.com

再帰関数vssetInterval vs setTimeout javascript

nodeJを使用していて、無限関数を呼び出す必要がありますが、最適なパフォーマンスを得るには何が最適かわかりません。

再帰関数

function test(){
//my code
test();
}

setInterval

setInterval(function(){
//my code
},60);

setTimeout

function test(){
//my code
setTimeout(test,60);
}

サーバーを崩壊させずに最高のパフォーマンスが欲しい。私のコードにはいくつかの算術演算があります。

javascriptのパフォーマンスを最適化するための提案に感謝します。

10
J261

注意してください..最初のコードはJavaScriptイベントループをブロックします。

基本的にJSには、処理する必要のある関数のリストのようなものがあります。 setTimeoutsetInterval、またはprocess.nextTickを呼び出すと、このリストに特定の関数が追加され、適切なタイミングで処理されます。

最初のケースのコードは停止しないため、イベントリスト内の別の関数を処理することはできません。

2番目と3番目のケースは良いです..1つの小さな違いがあります。

たとえば、関数の処理に10ミリ秒かかり、間隔が60ミリ秒になる場合。

  • setIntervalを持つ関数は、0-10、60-70、120-130、...の時間で処理されます(したがって、呼び出し間の遅延は50ミリ秒のみです)
  • ただし、setTimeoutを使用すると、次のようになります。
    • 最初にfuncを呼び出す場合:0-10、70-80、140-150、210-220、.。
    • 最初にsetTimeoutを呼び出す場合:60-70、130-140、200-210、.。

したがって、違いは関数の開始間の遅延であり、ゲーム、オークション、株式市場などの一部の間隔ベースのシステムで重要になる可能性があります。

再帰で頑張ってください:-)

16
Jan Jůna

すでに述べたように、無限の再帰関数はスタックオーバーフローにつながります。時間トリガーコールバックは、クリアスタックを使用して独自のコンテキストで実行されます。

setIntervalは、再帰的なsetTimeoutを介したより正確な定期的な呼び出しに役立ちますが、欠点があります。キャッチされない例外がスローされた場合でもコールバックがトリガーされます。これにより、通常、60ミリ秒ごとに数バイトの長さのログエントリが生成され、1日あたり1'440'000エントリになります。さらに、負荷の高いsetIntervalコールバックは、応答しないスクリプトまたはホールシステムになってしまう可能性があります。

関数から戻る直前の再帰的なsetTimeoutは、例外がキャッチされていない場合、not実行されます。関数の実行時間とは関係なく、コールバック関数から戻った後の他のタスクの時間枠を保証します。

10

何を達成しようとしているのかわかりませんが、再帰を使用する「安全な」方法は次のとおりです... https://stackoverflow.com/questions/24208676/how-to-use-recursion-in-javascript/24208677

/*
this will obviously crash... and all recursion is at risk of running out of call stack and breaking your page...

function recursion(c){
    c = c || 0;
    console.log(c++);
    recursion(c);
}
recursion();

*/

// add a setTimeout to reset the call stack and it will run "forever" without breaking your page!
// use chrome's heap snapshot tool to prove it to yourself.  :)

function recursion(c){
    setTimeout(function(c){
        c = c || 0;
        console.log(c++);
        recursion(c);
    },0,c);
}

recursion();

// another approach is to use event handlers, but that ultimately uses more code and more resources
7
chad steele

Jan Juna で記述された「パフォーマンス遅延」を想定して、この簡単なスクリプトを試して、スループットに違いがあるかどうかを確認しました。

間隔:

const max = 50;
const timer = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100, 150, 200, 250, 300, 400, 500];

function crono(timer) {
  return new Promise(resolve => {
    const exit = [];
    let i = 0
    setInterval(() => i++, timer);

    setInterval(() => {
      exit.Push(i);
      i = 0;
      if (exit.length === max) {
        const sum = exit.reduce((a, b) => (a + b), 0);
        const avg = sum / exit.length;
        console.log(`${timer} = ${avg}`);
        resolve(avg)
      }
    }, 1000);
  });
}

Promise.all(timer.map(crono)).then(process.exit);

タイムアウト:

const max = 50;
const timer = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100, 150, 200, 250, 300, 400, 500];

function crono(timer) {
  return new Promise(resolve => {
    const exit = [];
    let i = 0

    const redo = () => {
      i++
      setTimeout(redo, timer);
    }

    setInterval(() => {
      exit.Push(i);
      i = 0;
      if (exit.length === max) {
        const sum = exit.reduce((a, b) => (a + b), 0);
        const avg = sum / exit.length;
        console.log(`${timer} = ${avg}`);
        resolve(x)
      }
    }, 1000);

    redo();
  });
}

Promise.all(timer.map(crono)).then(process.exit);

そして、これはnodejs 8.11の出力であり、スループットの点で違いはありません。

results

1
Manuel Spigolon

再帰関数はスタックオーバーフローを引き起こします。それはあなたが望むものではありません。

示したsetIntervalsetTimeoutの方法は、setIntervalがより明確であることを除いて、同じです。

setIntervalをお勧めします。 (結局のところ、それが目的です。)

0
Paul Draper

再帰的なsetTimeoutは実行間の遅延を保証しますが、setInterval –は保証しません。

2つのコードフラグメントを比較してみましょう。最初のものはsetIntervalを使用します:

_let i = 1;
setInterval(function() {
  func(i);
}, 100);
_

2つ目は再帰的なsetTimeoutを使用します。

_let i = 1;
setTimeout(function run() {
  func(i);
  setTimeout(run, 100);
}, 100);
_

setIntervalの場合、内部スケジューラは100msごとにfunc(i)を実行します。

funcsetInterval呼び出し間の実際の遅延は、コードよりも短くなっています。

_func's_の実行にかかる時間は間隔の一部を「消費」するため、これは正常です。

_func's_の実行が予想よりも長く、100ミリ秒以上かかる可能性があります。

この場合、エンジンはfuncが完了するのを待ってからスケジューラーをチェックし、時間が経過した場合はすぐに再度実行します。

Edgeの場合、関数が常にdelay msより長く実行されると、呼び出しは一時停止することなく行われます。

再帰的なsetTimeoutは、固定されたdelay(ここでは100ms)を保証します。

0
solanki...