带延迟串行执行异步函数
Executing asynchronous functions serially with a delay
最初我有这样的东西(ads是一个数组):
for (var i = 0; i < ads.length; i++) {
asynchMeth(ad[i]);
}
asyncMeth是一个调用服务器的异步方法(我不能使它同步)。但我希望asynchMeth(ad[I])将在asynchMeth(ad[I-1])完成后3秒启动。以下不起作用,但它给出了我想要的东西的想法:
isWorking = false; //Will be set to true whenever asyncMeth starts, to false when it ends.
var i = 0;
var timer = setInterval(3000, function() {
if(!isWorking){
if(i < ads.length){
asyncMeth(ads[i]);
i++;
}
else{
clearInterval(timer);
}
}
});
当我们调用具有不同/动态参数的函数时,如何使用setInterval?
我首先会向asyncMeth
添加一个回调,每当函数完成它正在做的任何事情时都会调用它。否则就无法知道asyncMeth
是否已经完成了它的工作。在你的例子中,你拥有的和你想要的不一样!您希望ads[i]
在ads[i - 1]
完成后处理3秒。相反,您所拥有的是在ads[i - 1]
开始处理后3秒开始处理ads[i]
的代码。通过回调asyncMeth
,您可以执行以下操作:
(function work(i) {
if(i < ads.length) {
asyncMeth(ads[i], function() {
work(++i);
});
}
}(0);
这里有一个自调用函数,它最初接受参数值0
。该值被指定给参数i
。如果i
小于ads.length
,则意味着我们仍有项目要处理。因此它以ads[i]
为参数调用asyncMeth
。
但我们也提供了回调。在这个回调中,我们告诉回调调用work
,并增加i
的值。这意味着work
将开始处理下一个项目。
因此,现在您可以一个接一个地异步处理每个项目,直到i
等于ads.length
,此时您已经处理了所有项目。
编辑
我注意到你提到你想要3秒的延迟。为此,你可以这样做:
(function work(i) {
if(i < ads.length) {
asyncMeth(ads[i], function() {
setTimeout(function() {
work(++i);
}, 3000);
});
}
}(0);
这里唯一的区别是,下一次迭代发生在上一次迭代完成后3秒。
AFAICT,这里有两个答案:你想要的答案和你需要的答案。如果其他人偶然发现谷歌的这个答案,我会同时给出这两个答案。
您应该做什么
看起来你正试图让一堆异步调用按顺序发生,并猜测它们会在大约三秒后返回。当然,如果这不是一个准确的猜测,你就会遇到问题,所以你真正想要的是一个在请求返回时立即触发的函数。
有两种方法可以做到这一点——要么是延续传递风格(又名回调地狱),要么是使用promise。前者在概念上更容易,但在匆忙中变得丑陋:
$http('url', function (data) {
//this function is called when the AJAX request comes back
//do something with data
$http('url2', function (data) {
//do something with second piece of data after request 2 loads
})
})
当你需要一个任意的数字时,这会变得非常混乱。我的首选解决方案是递归的高阶函数,如以下所示,尽管可以迭代执行:
var launch = function f (i) {
return function (data) {
//do something with data
if (i <= urls.length - 1)
$http(urls[i+1], f(i+1))
}
}
//manually trigger the first one
launch(0)()
更干净的方式是承诺。关于如何使用promise来清理异步代码,有数百个教程,下面是一个如何对不同数量的请求进行清理的示例。
你问了什么
如果你真的想用不同的参数启动一堆超时,那么诀窍是关闭每个超时来缓存i
的值。如果你这样做:
for (var i = 0; i < 20; i++) {
setTimeout(function () {
//i is 20 by the time this runs...
}, 1000)
}
那么该函数的所有20次调用都会将i
看作20,这可能不是您想要的。
解决方案是:
for (var i = 0; i < 20; i++) {
setTimeout((function (j) {
//do something with j, which is a copy of the value of i for this iteration
})(i), 1000)
}
这种方法之所以有效,是因为显式传递的基元函数参数(即数字)是按值传递的,而通过闭包"传递"变量(这就是断开的例子中发生的情况)是通过引用传递的,即使是基元也是如此。
- 如何在异步函数中使用javascript对象
- Meteor:异步函数回调异常:onAfterAction
- 未调用jquery异步函数
- 使用来自不同异步函数的响应创建一个json对象
- 如何在异步函数中正确使用Promise.all()和then()
- 从异步函数返回值
- 如何按照承诺使用mocha/chai/chai测试ES7异步函数
- 如何在将Node异步函数转换为promise时使用Q库
- 如何在异步函数与Deferredjquery之间同步
- ng用异步函数模拟怪异行为
- 用mocha测试异步函数
- 用于异步函数中的循环和定时问题
- Node.js设计:多个异步函数使用作为闭包传递的函数写入数据库
- 嵌套异步函数未及时返回数据的问题
- 在异步函数中维护变量的状态
- 递归异步函数
- Bluebird promisifyAll不会创建整个异步函数集
- 递归调用异步函数
- 异步函数调用是否可以在两个同步语句之间完成
- 如何在nodejs中实现异步函数