我需要一个循环,在继续之前等待异步调用。就像是:
for ( /* ... */ ) {
someFunction(param1, praram2, function(result) {
// Okay, for cycle could continue
})
}
alert("For cycle ended");
我怎么能这样做?你有什么想法?
我需要一个循环,在继续之前等待异步调用。就像是:
for ( /* ... */ ) {
someFunction(param1, praram2, function(result) {
// Okay, for cycle could continue
})
}
alert("For cycle ended");
我怎么能这样做?你有什么想法?
如果你阻止脚本,你就不能在 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;
}
这将为我们提供一个异步的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')}
);
和输出:
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
我简化了这个:
功能:
var asyncLoop = function(o){
var i=-1;
var loop = function(){
i++;
if(i==o.length){o.callback(); return;}
o.functionToLoop(loop, i);
}
loop();//init
}
用法:
asyncLoop({
length : 5,
functionToLoop : function(loop, i){
setTimeout(function(){
document.write('Iteration ' + i + ' <br>');
loop();
},1000);
},
callback : function(){
document.write('All done!');
}
});
示例: http : //jsfiddle.net/NXTv7/8/
@Ivo 建议的一个更简洁的替代方法是Asynchronous Method Queue,假设您只需要对集合进行一次异步调用。
(有关更详细的解释,请参阅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);
您只需创建 的新实例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);
}
这种模式的另一个好处是您可以将多个函数添加到队列中,而不仅仅是一个。
如果您有一个包含迭代器函数的对象,您可以在幕后添加对这个队列的支持并编写看起来同步但不是同步的代码:
MyClass.each(function(result){ ... })
只需写入each
将匿名函数放入队列而不是立即执行它,然后在异步调用完成时刷新队列。这是一个非常简单而强大的设计模式。
PS 如果您正在使用 jQuery,那么您已经有一个名为jQuery.Deferred的异步方法队列可供您使用。
也看看这个出色的库caolan / async。您的for
循环可以使用mapSeries或series轻松完成。
如果您的示例中有更多详细信息,我可以发布一些示例代码。
我们也可以使用 jquery.Deferred 的帮助。在这种情况下, asyncLoop 函数将如下所示:
asyncLoop = function(array, callback) {
var nextElement, thisIteration;
if (array.length > 0) nextElement = array.pop();
thisIteration = callback(nextElement);
$.when(thisIteration).done(function(response) {
// here we can check value of response in order to break or whatever
if (array.length > 0) asyncLoop(array, collection, callback);
});
};
回调函数将如下所示:
addEntry = function(newEntry) {
var deferred, duplicateEntry;
// on the next line we can perform some check, which may cause async response.
duplicateEntry = someCheckHere();
if (duplicateEntry === true) {
deferred = $.Deferred();
// here we launch some other function (e.g. $.ajax or popup window)
// which based on result must call deferred.resolve([opt args - response])
// when deferred.resolve is called "asyncLoop" will start new iteration
// example function:
exampleFunction(duplicateEntry, deferred);
return deferred;
} else {
return someActionIfNotDuplicate();
}
};
解决延迟的示例函数:
function exampleFunction(entry, deffered){
openModal({
title: "what should we do with duplicate"
options: [
{name:"Replace", action: function(){replace(entry);deffered.resolve(replace:true)}},
{name: "Keep Existing", action: function(){deffered.resolve(replace:false)}}
]
})
}