我使用jQuery制作各种ajax POST请求。我需要跟踪每一个的成功或失败,以及整个批处理的整体进度,以便我可以使用进度条更新UI以及有多少请求成功的信息,以及有多少请求失败,超出总数。
在尝试在我的应用程序中实现该功能之前,我已经在jsfiddle中使用了一些代码作为概念证明,到目前为止还没有运气。这就是我所拥有的:
// an alternative to console.log to see the log in the web page
var fnLog = function(message) {
$('#console').append($("<p>" + message + "</p>"));
};
// keeping track of how many ajax calls have been finished (successfully or not)
var count = 0;
// a dummy ajax call that succeeds by default
var fn = function(shouldFail) {
return $.get(shouldFail ? '/echo/fail/' : '/echo/json/')
.done(function() { fnLog("done") })
.fail(function() { fnLog("FAIL") });
};
// a set of different asynchronous ajax calls
var calls = [fn(),fn(),fn(),fn(true),fn(),fn()];
// an attempt to make a collective promise out of all the calls above
$.when.apply($, calls)
.done(function() { fnLog("all done") })
.fail(function() { fnLog("ALL FAIL") })
.always(function() { fnLog("always") })
.progress(function(arg) { fnLog("progress" + arg) })
.then(function() { fnLog("finished") });
这一切都在这个小提琴中:http://jsfiddle.net/mmtbo7v6/1/
我需要的是能够在所有承诺得到解决后(或者成功与否)提供应该调用的回调。
当上面的所有调用都设置为成功时(通过删除数组中第四个true
调用的fn
参数),它可以正常工作。输出打印以下内容:
done
done
done
done
done
done
all done
always
finished
但即使单个调用设置为失败(因为默认情况下在jsfiddle中),输出如下:
done
FAIL
ALL FAIL
always
done
done
done
done
因此,在解决所有promise之后,不会调用任何集合promise回调(由$.when
调用生成的回调)。如果单个ajax调用失败,则不会调用最终的.then
。
此外,我希望了解如何跟踪这批ajax调用的进度,以及更新UI中的进度条。
答案 0 :(得分:3)
首先是第一件事:
“所有承诺已经解决,但有些可能被拒绝”的问题通常被称为“解决方案”。我已经提供了类似问题here的答案,仅提供结果,here提供的实施方案可让您访问所有结果,甚至是被拒绝的结果。
function settle(promises){
var d = $.Deferred();
var counter = 0;
var results = Array(promises.length);
promises.forEach(function(p,i){
p.then(function(v){ // add as fulfilled
results[i] = {state:"fulfilled", promise : p, value: v};
}).catch(function(r){ // add as rejected
results[i] = {state:"rejected", promise : p, reason: r};
}).always(function(){ // when any promises resolved or failed
counter++; // notify the counter
if (counter === promises.length) {
d.resolve(results); // resolve the deferred.
}
});
});
return d.promise();
}
您可以使用settle
代替$.when
来获得所需的结果。
至于进展 - 我个人建议将渐进回调传递给方法本身。模式是这样的:
function settle(promises, progress){
progress = progress || function(){}; // in case omitted
var d = $.Deferred();
var counter = 0;
var results = Array(promises.length);
promises.forEach(function(p,i){
p.then(function(v){ // add as fulfilled
results[i] = {state:"fulfilled", promise : p, value: v};
}).catch(function(r){ // add as rejected
results[i] = {state:"rejected", promise : p, reason: r};
}).always(function(){ // when any promises resolved or failed
counter++; // notify the counter
progress((promises.length - counter) / promises.length);
if (counter === promises.length) {
d.resolve(results); // resolve the deferred.
}
});
});
return d.promise();
}
这会让你做类似的事情:
settle([url1, url2, ... url100].map($.get), function(soFar){
$("#myProgressBar").css("width", (soFar * 100)+"%");
}).then(function(results){
console.log("All settled", results);
]);
答案 1 :(得分:1)
事实证明,这是一个更好的替代方案,可以影响承诺的方法。看看两种模式的组合:Observables + Iterables = Reactive programming。
Reactive Programming是使用异步数据流进行编程,即将异步数据流视为可以作为传统集合数据类型遍历和转换的集合。 This article是一个很好的介绍。
我没有将此答案帖子转换为教程,所以让我们直接找到解决方案,如下所示。我将使用RxJS库,但JS中还有其他用于Reactive Programming的库(bacon.js似乎也很受欢迎)。
function settle(promises) {
return Rx.Observable.from(promises).concatMap(function(promise, index) {
return Rx.Observable.fromPromise(promise).
map(function(response) {
return { count: index+1, total: promises.length, state: "fulfilled", promise: promise, value: response };
}).
catch(function(reason) {
return Rx.Observable.of({ count: index+1, total: promises.length, state: "rejected", promise: promise, reason: reason });
});
});
}
函数本身返回一个observable,它是一个事件流。也就是说,每个承诺的事件都已成功完成,无论是否成功。我们可以使用返回的observable来收听此流(或订阅它,如果我们要遵守RxJS术语)。
var results = settle(promises);
results.subscribeOnNext(function(results) {
// process each result as it arrives
// progress info can be extracted from results.count and results.total
});
results.subscribeOnCompleted(function() {
// completion callback
});
就是这样。更清晰的代码,更多功能编程方法。无需保持状态,一切都以更具声明性的方式表达。只是我们想要完成的,不是应该如何完成。