15

我对如何setTimeout工作感到有点困惑。我试图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);
};

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

谢谢

4

8 回答 8

14

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

以下是一些实现:

使用递归

您可以在回调中使用递归调用。setTimeout

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清除调用堆栈(请参阅此问题),因此您不必担心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);
});

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

生成器函数文档

于 2015-06-01T14:03:36.250 回答
8

你需要这样的东西

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);    
    }
}

另见这个小提琴

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

于 2013-03-28T13:11:32.240 回答
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)
}

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

于 2013-03-28T13:21:32.453 回答
3

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

您的代码所做的是创建 2000 个(实际上是 1999 个)对象,这些对象将在 1 秒setTimeout调用该makeRequest函数。看,他们都不知道其他s 的存在。setTimeout

如果您希望它们彼此相隔 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秒。

于 2017-05-08T06:43:10.140 回答
1

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

setTimeout(makeRequest, 1000);

没有 ()

于 2013-03-28T13:05:22.510 回答
1

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

vat poller = setInterval(makeRequestFunc, 3000)

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

cleanInterval(poller)
于 2018-03-13T23:53:57.190 回答
0
        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'))
于 2020-04-30T15:03:53.437 回答
0

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

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
    });
}
于 2020-09-14T16:27:42.787 回答