跟踪各种 ajax post 请求的进度
Keeping track and progress of various ajax post requests
我正在使用 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") });
全在这fiddle:http://jsfiddle.net/mmtbo7v6/1/
我需要的是能够提供回调,该回调应在所有 promises 都已解决(无论成功与否)后调用。
当上面的所有调用都设置为成功时(通过删除数组中第四个 fn
调用的 true
参数)它工作正常。输出打印以下内容:
done
done
done
done
done
done
all done
always
finished
但是即使单个调用设置为失败(因为它在 jsfiddle 中默认设置),输出如下:
done
FAIL
ALL FAIL
always
done
done
done
done
因此 none 的集体承诺回调(由 $.when
调用生成的回调)在所有承诺都得到解决后被调用。如果单个 ajax 调用失败,则根本不会调用最后的 .then
。
此外,我希望了解如何跟踪这批 ajax 调用的进度,以更新 UI 中的进度条。
嗯...我要不公平了。 jQuery 实际上与进度事件捆绑在一起,但我自己讨厌它们,因为我认为它们不能很好地组合或聚合 - 所以我将展示一个更简单的进度条替代方法,我 believe is superior 相反。
要事第一:
'all promises resolved but some possibly rejected' 问题通常称为 'settle'。我已经提供了一个类似问题的答案 here with just giving the results and 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);
]);
事实证明,这个问题有一个更好的替代方法,它可以替代 promises 方法。看看两种模式的组合:Observables + Iterables = Reactive programming.
Reactive Programming是用异步数据流编程,即将异步数据流当作集合来处理,可以像传统的集合数据类型一样进行遍历和转换。 This article 是一个很好的介绍。
我不会把这个答案post变成教程,所以我们直接上解决方案,如下所示。我要使用 RxJS library, but there are other libraries for Reactive Programming in JS (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 });
});
});
}
函数本身 returns 一个可观察对象,它是一个事件流。即,每个 promise 的事件都已完成,无论成功与否。我们可以使用返回的 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
});
就是这样。更简洁的代码,更函数式的编程方法。无需保持状态,一切都以更明确的方式表达。只是我们想要完成的,而不是应该如何完成。
我正在使用 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") });
全在这fiddle:http://jsfiddle.net/mmtbo7v6/1/
我需要的是能够提供回调,该回调应在所有 promises 都已解决(无论成功与否)后调用。
当上面的所有调用都设置为成功时(通过删除数组中第四个 fn
调用的 true
参数)它工作正常。输出打印以下内容:
done
done
done
done
done
done
all done
always
finished
但是即使单个调用设置为失败(因为它在 jsfiddle 中默认设置),输出如下:
done
FAIL
ALL FAIL
always
done
done
done
done
因此 none 的集体承诺回调(由 $.when
调用生成的回调)在所有承诺都得到解决后被调用。如果单个 ajax 调用失败,则根本不会调用最后的 .then
。
此外,我希望了解如何跟踪这批 ajax 调用的进度,以更新 UI 中的进度条。
嗯...我要不公平了。 jQuery 实际上与进度事件捆绑在一起,但我自己讨厌它们,因为我认为它们不能很好地组合或聚合 - 所以我将展示一个更简单的进度条替代方法,我 believe is superior 相反。
要事第一:
'all promises resolved but some possibly rejected' 问题通常称为 'settle'。我已经提供了一个类似问题的答案 here with just giving the results and 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);
]);
事实证明,这个问题有一个更好的替代方法,它可以替代 promises 方法。看看两种模式的组合:Observables + Iterables = Reactive programming.
Reactive Programming是用异步数据流编程,即将异步数据流当作集合来处理,可以像传统的集合数据类型一样进行遍历和转换。 This article 是一个很好的介绍。
我不会把这个答案post变成教程,所以我们直接上解决方案,如下所示。我要使用 RxJS library, but there are other libraries for Reactive Programming in JS (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 });
});
});
}
函数本身 returns 一个可观察对象,它是一个事件流。即,每个 promise 的事件都已完成,无论成功与否。我们可以使用返回的 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
});
就是这样。更简洁的代码,更函数式的编程方法。无需保持状态,一切都以更明确的方式表达。只是我们想要完成的,而不是应该如何完成。