我想使用两个异步调用来获取两个资源。我想只在检索到两个资源时继续。
我怎样才能在JS中优雅地做到这一点?
这样可行:
getStuff1(function (result1) {
getStuff2 (function (result2) {
// do stuff with result1 and result2
....
}
}
但stuff2仅在stuff1完成后才开始。我宁愿在等待stuff1时启动stuff2。
答案 0 :(得分:18)
如果你知道Javascript中函数实际上是一流的对象,你可以提出一个相当优雅的解决方案。
不任何额外对象或全局变量。
function callback1() {
callback1.done = true;
commonCallback();
}
function callback2() {
callback2.done = true;
commonCallback();
}
function commonCallback() {
if (callback1.done && callback2.done) {
// do stuff, since you know both calls have come back.
}
}
为什么这么优雅?因为您封装了数据,所以范围是免费的来自无用的变量,并且代码更具可读性。 酷是多少? :)
更新
如果您想要更通用的解决方案,可以尝试以下方法:
function callback() {
callback.number -= 1;
if (callback.number === 0) {
// do stuff since all calls finished
callback.last();
}
}
callback.newQueue = function(num, last) {
callback.number = num;
callback.last = last;
}
// EXAMPLE USAGE
// our last function to be invoked
function afterEverythingDone(){ alert("done"); }
// create a new callback queue
callback.newQueue(3, afterEverythingDone);
// as time passes you call the callback
// function after every request
callback();
callback();
callback();
// after all call is finished
// afterEverythingDone() executes
再次出色:)
答案 1 :(得分:3)
一种方法是对两个请求使用相同的回调,并在两者完成时继续:
var requestStatus = {
fooComplete: false,
barComplete: false
};
function callback(data) {
if (isFoo(data)) {
requestStatus.fooComplete = true;
} else if (isBar(data)) {
requestStatus.barComplete = true;
}
if (requestStatus.fooComplete && requestStatus.barComplete) {
proceed();
}
}
getAsync("foo", callback);
getAsync("bar", callback);
你可能想把它变成一个课堂。
修改:为清晰起见添加了异步调用
答案 2 :(得分:3)
您可以让每个回调函数指示它们各自的请求已经返回,然后执行相同的公共函数。举例说明:
var call1isBack = false;
var call2isBack = false;
function call1Callback() {
call1isBack = true;
commonCallback();
}
function call2Callback() {
call2isBack = true;
commonCallback();
}
function commonCallback() {
if (call1isBack && call2isBack) {
// do stuff, since you know both calls have come back.
}
}
答案 3 :(得分:1)
使用带有计数器的公共回调处理程序,该计数器只允许在计数器达到或超过待处理请求数后进入“实际”处理部分:
var commonHandler = (function() {
var counter=0, pendingCalls=2;
return function() {
if (++counter >= pendingCalls) {
// Do the actual thing...
}
}
})();
makeAjaxCall({args:args1, onComplete:commonHandler});
makeAjaxCall({args:args2, onComplete:commonHandler});
使用匿名函数周围的闭包可以避免为计数器使用全局变量。
答案 4 :(得分:0)
这是我正在处理的并发库的片段。您需要做的就是实例化一个新的Concurrent.Counter,其中包含等待的请求数(在执行它们之前),以及在完成后执行的回调。在每个异步函数返回之前,让它调用计数器的decrement()方法;一旦计数器减少了指定的次数,就会执行回调:
// Ensure the existence of the "Concurrent" namespace
var Concurrent = Concurrent || {};
/**
* Constructs a new Concurrent.Counter which executes a callback once a given number of threads have
* returned. Each Concurrent.Counter instance is designed to be used only once, and then disposed of,
* so a new one should be instantiated each additional time one is needed.
*
* @param {function} callback The callback to execute once all the threads have returned
* @param {number} count The number of threads to await termination before executing the callback
*/
Concurrent.Counter = function(callback, count) {
/**
* Decrements the thread count, and executes the callback once the count reaches zero.
*/
this.decrement = function() {
if (!(-- count)) {
callback();
}
};
};
// The number of asynchronous requests to execute
var requests = 10,
// Executes a callback once all the request tasks have returned
counter = new Concurrent.Counter(function() {
// this will be executed once the tasks have completed
}, requests),
// Tracks the number of requests made
i;
for (i = 0; i < requests; i ++) {
setTimeout(function() {
/*
* Perform an asynchronous task
*/
// Decrement the counter
counter.decrement();
}, 0);
}
答案 5 :(得分:0)
这是我的头脑,但它应该有效。
function createCoordinator(onFinish) {
var count = 0;
return function (callback) {
count++;
return function () {
if (callback.apply(this, arguments))
count--;
if (count == 0)
onFinish();
}
}
}
var coordinate = createCoordinator(function () { alert('done!') });
// Assume sendAJAX = function (url, onreadystatechange)
sendAJAX('url1', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url1!');
return true;
}));
sendAJAX('url2', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url2!');
return true;
}));