2013-03-28 120 views
9

我对setTimeout的工作原理有点困惑。我试图让一个setTimeout在一个循环中,以便循环迭代相隔1s。 每个循环迭代都会发出一个HTTP请求,看起来另一端的服务器无法在如此短的时间内处理这么多的请求。Node.js循环中的setTimeout

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

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

为什么这不起作用,我该如何做到这一点?

谢谢

回答

6

你需要像这样

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);的位置,您可以打电话到服务器。 请注意,计数器相反(倒数)。我更新了一些 评论在哪里做你的事情。

+0

这很好。非常感谢。 – glasspill

+0

欢迎。如果您对服务器的请求不是时间关键的,那么您最好使用一种结构,只有在先前的服务器调用完成后才启动新的定时器。然后,即使呼叫时间超过1秒(setTimeout计时器值),也只有一个服务器呼叫处于活动状态。 –

+0

** WARNIG **:您的解决方案可能导致RangeError:超出最大呼叫堆栈大小。请参阅使用'setImmediate'的[此解决方案](http://stackoverflow.com/a/30575612/1480391)。 BTW OP正在使用Node.js ..(Node.js中没有'alert')。 –

1

你在你的setTimeout调用调用makeRequest的() - 你应该函数传递给setTimeout的,而不是调用它,所以像

setTimeout(makeRequest, 1000); 

没有()

2

现在,您正在计划所有请求在脚本运行后的同一时间发生。您需要执行以下操作:

var numRequests = 2000, 
    cur = 1; 

function scheduleRequest() { 
    if (cur > numRequests) return; 

    makeRequest({ 
     host: 'www.host.com', 
     path: '/path/' + cur 
    }, cur); 

    cur++; 
    setTimeout(scheduleRequest, 1000) 
} 

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

+0

这就是我的想法 – glasspill

+0

@ jmar777我意识到这是一个旧帖子,但我有类似的问题。你介意回答为什么'scheduleRequest'函数没有通过'cur'或'numRequests'? –

10

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( “下一个” 节点。

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

BTW:使用ES6),你可以用优雅的解决方案解决您的问题,而递归JS这是真正使用循环;)

Generator functions documentation

0

我可能会在派对上迟到,但这里是另一个(更具可读性)的解决方案,无需忽略for循环。

你的代码不会是创建2000(实际1999)setTimeout对象1秒后将调用makeRequest功能从现在开始。看,他们都不知道其他setTimeout s的存在。

如果你想让它们彼此隔开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秒彼此分开。