【问题标题】:setTimeout in Node.js loopNode.js 循环中的 setTimeout
【发布时间】:2013-03-18 22:21:20
【问题描述】:

我对@9​​87654321@ 的工作原理有点困惑。我试图在循环中有一个setTimeout,以便循环迭代相隔1秒。 每次循环迭代都会发出一个 HTTP 请求,而另一端的服务器似乎无法在如此短的时间内处理那么多请求。

for (var i = 1; i<=2000 && ok; i++) {
    var options = {
        host:'www.host.com',
        path:'/path/'+i
    };

    setTimeout(makeRequest(options, i), 1000);
};

为什么这不起作用,我该如何实现?

谢谢

【问题讨论】:

    标签: javascript node.js asynchronous settimeout


    【解决方案1】:

    setTimeout 非阻塞,它是异步的。你给它一个回调,当延迟结束时,你的回调就会被调用。

    这里有一些实现:

    使用递归

    您可以在setTimeout 回调中使用recursive call

    function waitAndDo(times) {
      if(times < 1) {
        return;
      }
    
      setTimeout(function() {
    
        // Do something here
        console.log('Doing a request');
    
        waitAndDo(times-1);
      }, 1000);
    }
    

    这里是如何使用你的函数:

    waitAndDo(2000); // Do it 2000 times
    

    关于堆栈溢出错误setTimeout 清除调用堆栈(请参阅this question),因此您不必担心setTimeout 递归调用的堆栈溢出。

    使用生成器(io.js、ES6)

    如果您已经在使用io.js(使用ES6 的“下一个”Node.js),您可以通过一个优雅的解决方案来解决您的问题,而无需递归:

    function* waitAndDo(times) {
      for(var i=0; i<times; i++) {
    
        // Sleep
        yield function(callback) {
          setTimeout(callback, 1000);
        }    
    
        // Do something here
        console.log('Doing a request');
      }
    }
    

    这里是如何使用你的函数(co):

    var co = require('co');
    
    co(function* () {
      yield waitAndDo(10);
    });
    

    顺便说一句:这实际上是在使用循环;)

    Generator functions documentation.

    【讨论】:

      【解决方案2】:

      你需要这样的东西

      var counter = 5;
      
      function makeRequst(options, i) {
          // do your request here
      }
      
      function myFunction() {
          alert(counter);
      
          // create options object here
          //var options = {
          //    host:'www.host.com',
          //    path:'/path/'+counter
          //};
          //makeRequest(options, counter);
      
          counter--;
          if (counter > 0) {
              setTimeout(myFunction, 1000);    
          }
      }
      

      另见this fiddle

      alert(count); 处,您可以调用服务器。 请注意,计数器的工作方式相反(倒计时)。我更新了一些 cmets 在哪里做你的事。

      【讨论】:

      • 欢迎。如果您对服务器的请求不是时间关键的,则最好使用仅在前一个服务器调用完成后才启动新计时器的结构。那么即使调用时间超过 1 秒(setTimeout 计时器值),也只有一个服务器调用处于活动状态
      • 警告:您的解决方案可能会导致RangeError: Maximum call stack size exceeded。请参阅使用setImmediatethis solution。 BTW OP 正在使用 Node.js..(Node.js 中没有 alert)。
      • @Yves M. :setTimeout() 应该足够了。此解决方案中没有对myFunction 的递归调用。
      • @Yves M.:我同意,setTimeout 调用你给它的函数,但在 Node 中,它在事件循环的不同迭代中这样做,所以 myFunction 实际上并没有调用自己。例如,在 20 929 次调用后,对我进行直接递归调用失败。使用 setTimeout 清除了 500 000 个调用,没有失败的迹象。
      • @MikeTunnicliffe 是的,对不起,我的错误:D setTimeout 确实清除了调用堆栈。我很抱歉。阅读:stackoverflow.com/questions/8058612/… :D 您的代码不会堆栈溢出... :)
      【解决方案3】:

      现在,您正在安排所有请求同时发生,就在脚本运行后一秒钟。您需要执行以下操作:

      var numRequests = 2000,
          cur = 1;
      
      function scheduleRequest() {
          if (cur > numRequests) return;
      
          makeRequest({
              host: 'www.host.com',
              path: '/path/' + cur
          }, cur);
      
          cur++;
          setTimeout(scheduleRequest, 1000)
      }
      

      请注意,每个后续请求仅在当前请求完成后安排。

      【讨论】:

      • 我也是这么想的
      • @jmar777 我意识到这是一个旧帖子,但是我遇到了类似的问题。你介意回答为什么scheduleRequest 函数没有通过curnumRequests
      【解决方案4】:

      我可能会迟到,但这里有另一个(更易读的)解决方案,无需省略 for 循环。

      您的代码所做的是创建 2000 个(实际上是 1999 年)setTimeout 对象,这些对象将在 1 秒后调用 makeRequest 函数从现在开始。看,他们都不知道另一个setTimeouts的存在。

      如果您希望它们彼此间隔 1 秒,则您有责任创建它们。

      这可以通过使用您的计数器(在本例中为i)和超时延迟来实现>.

      for (var i = 1; i<=2000 && ok; i++) {
          var options = {
              host:'www.host.com',
              path:'/path/'+i
          };
      
          setTimeout(makeRequest(options, i), i * 1000); //Note i * 1000
      };
      

      第一个超时对象将从现在开始设置为 1 秒,第二个将设置为从现在开始 2 秒,以此类推;意思是1秒彼此相隔。

      【讨论】:

        【解决方案5】:

        您在 setTimeout 调用中调用 makeRequest() - 您应该将函数传递给 setTimeout,而不是调用它,所以类似于

        setTimeout(makeRequest, 1000);
        

        没有 ()

        【讨论】:

          【解决方案6】:

          我很惊讶上面没有人提到这一点,但听起来你需要 setInterval 而不是 setTimeout。

          vat poller = setInterval(makeRequestFunc, 3000)
          

          上面的代码将每 3 秒发出一次请求。由于您将对象保存到变量poller,因此您可以通过清除对象来停止轮询,如下所示:

          cleanInterval(poller)
          

          【讨论】:

            【解决方案7】:
                    let i = 20;
                    let p = Promise.resolve(i)
                    while (i > 0) {
                    (i => {
                        p = p.then(() => {
                        return new Promise(function (resolve, reject) {
                            setTimeout(function () {
                                console.log(i);
                            resolve()
                            }, 2000)
                        })
                        })
                    })(i)
                    i--
                    }
                    p = p.then(data => console.log('execution ends'))
            

            【讨论】:

              【解决方案8】:

              我在这个主题上已经很晚了(像往常一样......;),但我发现将请求循环到慢速响应 API 并在没有 HTTP 504 的情况下获得响应的唯一方法是使用 Promise。

              async function LoadDataFromAPI(myParametersArray) {
                  for(var i = 0; i < myParametersArray.length; i++) { 
                      var x = await RunOneRequest(myParametersArray[i]); 
                      console.log(x); // ok
                  }
              }
              

              异步函数调用的函数:

              function RunOneRequest(parameter) {
                  return new Promise(resolve => {
                      setTimeout(() => {
                          request(parameter, (error, response, body) => {
                              // your request
                          });
                      resolve('ok);
                      }, 2000); // 2 secs
                  });
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-07-31
                • 1970-01-01
                • 2020-04-20
                • 1970-01-01
                • 1970-01-01
                • 2016-07-01
                • 1970-01-01
                相关资源
                最近更新 更多