以延迟顺序执行异步函数

时间:2013-12-04 20:57:28

标签: javascript parameters setinterval delayed-execution

最初我有类似的东西(广告是一个数组):

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?

2 个答案:

答案 0 :(得分:0)

我首先会向asyncMeth添加一个回调函数,只要函数完成它正在执行的任何操作,就会调用它。否则,无法知道asyncMeth是否已完成其工作。在你的例子中,你所拥有的与你想要的不一样!您希望ads[i]ads[i - 1]完成后3秒处理{/ 1}}。相反,您拥有的代码是在ads[i] 开始处理之后3秒开始处理ads[i - 1]的代码。通过回调asyncMeth,您可以执行此操作:

(function work(i) {
    if(i < ads.length) {
        asyncMeth(ads[i], function() {
            work(++i);
        });
    }
}(0);

这里有一个自调用函数,最初接受参数值0。该值分配给参数i。如果i小于ads.length,则表示我们仍有待处理的项目。因此,它将asyncMeth作为参数调用ads[i]

但是我们也提供回调。在此回调中,我们告诉回调调用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秒发生。

答案 1 :(得分:0)

AFAICT,这里有两个答案:你要求的答案,以及你需要的答案。我会给两者以防万一其他人偶然发现Google的答案。

你应该做什么

看起来你正试图让一堆异步调用按顺序发生,并猜测它们会在大约三秒内回来。当然,如果事实证明这不是一个准确的猜测,那么你将遇到问题,所以你真正想要的是一个在请求回来的瞬间触发的函数。

有两种方法可以做到这一点 - 延续传递风格(又名回调地狱)或者承诺。前者在概念上更容易,但匆忙变得丑陋:

$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)()

更清洁的方式是承诺。有数百个关于如何使用promises来清理异步代码的教程,here是如何为可变数量的请求执行此操作的示例。

您提出的问题

如果你真的想要发布一堆具有不同参数的超时,那么诀窍是关闭每一个超时以缓存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)
}

这个工作的原因是显式传递的原始函数参数(即数字)是通过值传递的,而通过闭包“传递”变量(这是在破碎的示例中发生的情况)通过引用传递,即使对于基元也是如此。