【问题标题】:recursive function vs setInterval vs setTimeout javascript递归函数 vs setInterval vs setTimeout javascript
【发布时间】:2014-06-04 08:58:04
【问题描述】:

我正在使用 NodeJs,需要调用一个无限函数,但我不知道什么是最佳性能。

递归函数

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

设置间隔

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

设置超时

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

我希望在不崩溃服务器的情况下获得最佳性能。我的代码有几个算术运算。

感谢任何优化 javascript 性能的建议。

【问题讨论】:

  • 取决于你调用的函数

标签: javascript performance recursion settimeout setinterval


【解决方案1】:

小心……你的第一个代码会阻塞 JavaScript 事件循环。

在 JS 中基本上是应该处理的函数列表。当您调用setTimeoutsetIntervalprocess.nextTick 时,您会将给定的函数添加到此列表中,当合适的时间到来时,它将被处理..

您的代码在第一种情况下永远不会停止,因此它永远不会让事件列表中的其他函数被处理。

第二种和第三种情况都不错..有一点区别。

如果你的函数需要处理例如 10ms 并且间隔将是你的 60ms..

  • 带有 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, ...

所以区别在于函数启动之间的延迟,这在某些基于间隔的系统中可能很重要,例如游戏、拍卖、股票市场……等等。

祝你递归顺利:-)

【讨论】:

  • 这是最完整和正确的答案,应该被接受为 imo 的答案。谢谢!
  • 我明白了,您说的是 cca 10ms 在函数内执行执行。但另一个显着的区别是:第一个示例 (setInterval) 不会立即执行,而是在 60 毫秒后执行。第二个示例 (setTimeout) 将在调用函数 test() 时立即执行,然后每 60 毫秒执行一次。
【解决方案2】:

如前所述,无限递归函数会导致堆栈溢出。时间触发的回调将在具有明确堆栈的自己的上下文中执行。

setInterval 对递归setTimeout 进行更准确的定期调用很有用,但是,有一个缺点:即使抛出未捕获的异常也会触发回调。这通常每 60 毫秒产生几个字节长的日志条目,每天 1'440'000 个条目。此外,负载繁重的setInterval 回调可能会导致脚本无响应甚至漏洞系统。

如果没有捕获到任何异常,将在函数返回之前立即执行递归setTimeout。它将保证从回调函数返回后其他任务的时间范围与函数的执行时间无关。

【讨论】:

    【解决方案3】:

    不确定您要完成什么,但这是使用递归的“安全”方式...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
    

    【讨论】:

      【解决方案4】:

      递归 setTimeout 保证执行之间的延迟,setInterval - 没有。

      让我们比较两个代码片段。第一个使用setInterval

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

      第二个使用递归setTimeout

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

      对于 setInterval,内部调度程序将每 100 毫秒运行一次 func(i)

      func 调用 setInterval 之间的实际延迟比代码中的要小!

      这很正常,因为func's 执行所花费的时间“消耗”了部分时间间隔。

      func's 的执行时间可能比我们预期的要长,并且需要超过 100 毫秒。

      在这种情况下,引擎等待func 完成,然后检查调度程序,如果时间到了,立即再次运行。

      在边缘情况下,如果函数的执行时间总是超过delay ms,那么调用将不会暂停。

      递归的setTimeout保证固定的delay(这里是100ms)。

      【讨论】:

        【解决方案5】:

        假设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 的输出:在吞吐量方面没有差异:

        【讨论】:

          【解决方案6】:

          递归函数导致堆栈溢出。这不是你想要的。

          您展示的setIntervalsetTimeout 方式是相同的,只是setInterval 更清晰。

          我推荐setInterval。 (毕竟,这就是它的用途。)

          【讨论】:

          • 你对递归函数是正确的,它被删除了。但是 setinterval over call 呢?
          • @Vorge,什么是“descarted”,什么是“call”?
          • 对不起,“丢弃”“递归函数”。在做了一些测试之后,我得出的结论是最好的是 setTimeout。这样我们就可以防止任何超叫。
          【解决方案7】:

          //使用setInterval函数

          setInterval(function(){Property=value;},1000);
          

          1000ms=1 秒; style.width 等属性;

          【讨论】:

          • 欢迎来到 Stack Overflow!这个问题是关于 Node.js,一种服务器端技术(不在浏览器中)。如果您认为 setInterval() 是“最适合...最佳性能”(如问题所问),请尝试解释 为什么 它的性能优于其他选项。尽情享受您在网站上的时光吧!
          猜你喜欢
          • 2011-12-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-11
          相关资源
          最近更新 更多