在JavaScript中循环异步

Mar*_*ser 87 javascript

在继续之前,我需要一个等待异步调用的循环.就像是:

for ( /* ... */ ) {

  someFunction(param1, praram2, function(result) {

    // Okay, for cycle could continue

  })

}

alert("For cycle ended");
Run Code Online (Sandbox Code Playgroud)

我怎么能这样做?你有什么想法?

Ivo*_*zel 182

如果阻止脚本,则无法在JavaScript中混合同步和异步,阻止浏览器.

你需要在这里采用完整的事件驱动方式,幸运的是我们可以隐藏丑陋的东西.

编辑:更新了代码.

function asyncLoop(iterations, func, callback) {
    var index = 0;
    var done = false;
    var loop = {
        next: function() {
            if (done) {
                return;
            }

            if (index < iterations) {
                index++;
                func(loop);

            } else {
                done = true;
                callback();
            }
        },

        iteration: function() {
            return index - 1;
        },

        break: function() {
            done = true;
            callback();
        }
    };
    loop.next();
    return loop;
}
Run Code Online (Sandbox Code Playgroud)

这将为我们提供异步loop,您当然可以进一步修改它以获取例如检查循环条件等的函数.

现在进行测试:

function someFunction(a, b, callback) {
    console.log('Hey doing some stuff!');
    callback();
}

asyncLoop(10, function(loop) {
    someFunction(1, 2, function(result) {

        // log the iteration
        console.log(loop.iteration());

        // Okay, for cycle could continue
        loop.next();
    })},
    function(){console.log('cycle ended')}
);
Run Code Online (Sandbox Code Playgroud)

并输出:

Hey doing some stuff!
0
Hey doing some stuff!
1
Hey doing some stuff!
2
Hey doing some stuff!
3
Hey doing some stuff!
4
Hey doing some stuff!
5
Hey doing some stuff!
6
Hey doing some stuff!
7
Hey doing some stuff!
8
Hey doing some stuff!
9
cycle ended
Run Code Online (Sandbox Code Playgroud)

  • 也许我错过了一些东西,但我不明白这实际上是如何同步的.你不需要setTimeout或其他东西吗?我尝试了你的代码,取出了console.log,并且大量调整了计数,它只是冻结了浏览器. (28认同)
  • 对不起,由于这实际上不是异步,不得不downvote. (4认同)
  • 就像上面的火箭快速说的那样,这个答案不是异步的,因此是完全错误的! (2认同)

wil*_*age 44

我简化了这个:

功能:

var asyncLoop = function(o){
    var i=-1;

    var loop = function(){
        i++;
        if(i==o.length){o.callback(); return;}
        o.functionToLoop(loop, i);
    } 
    loop();//init
}
Run Code Online (Sandbox Code Playgroud)

用法:

asyncLoop({
    length : 5,
    functionToLoop : function(loop, i){
        setTimeout(function(){
            document.write('Iteration ' + i + ' <br>');
            loop();
        },1000);
    },
    callback : function(){
        document.write('All done!');
    }    
});
Run Code Online (Sandbox Code Playgroud)

示例: http ://jsfiddle.net/NXTv7/8/


Ada*_*sek 7

什么@Ivo已经提出了一个更清洁的替代将是一个异步方法队列,假设你只需要进行一个异步调用的集合.

(请参阅Dustin Diaz的这篇文章以获得更详细的解释)

function Queue() {
  this._methods = [];
  this._response = null;
  this._flushed = false;
}

(function(Q){

  Q.add = function (fn) {
    if (this._flushed) fn(this._response);
    else this._methods.push(fn);
  }

  Q.flush = function (response) {
    if (this._flushed) return;
    this._response = response;
    while (this._methods[0]) {
      this._methods.shift()(response);
    }
    this._flushed = true;
  }

})(Queue.prototype);
Run Code Online (Sandbox Code Playgroud)

您只需创建一个新实例Queue,添加所需的回调,然后使用异步响应刷新队列.

var queue = new Queue();

queue.add(function(results){
  for (var result in results) {
    // normal loop operation here
  }
});

someFunction(param1, param2, function(results) {
  queue.flush(results);
}
Run Code Online (Sandbox Code Playgroud)

此模式的另一个好处是您可以向队列添加多个功能,而不只是一个.

如果你有一个包含迭代器函数的对象,你可以在幕后添加对这个队列的支持,并编写看起来是同步的代码,但不是:

MyClass.each(function(result){ ... })
Run Code Online (Sandbox Code Playgroud)

只需编写each将匿名函数放入队列而不是立即执行,然后在异步调用完成时刷新队列.这是一种非常简单而强大的设计模式.

PS如果您正在使用jQuery,那么您已经拥有了一个名为jQuery.Deferred的异步方法队列.