跟踪各种ajax发布请求的跟踪和进度

时间:2015-01-21 18:24:40

标签: javascript jquery ajax progress-bar promise

我使用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中的进度条。

2 个答案:

答案 0 :(得分:3)

好吧......我会不公平的。 jQuery实际上与升级事件捆绑在一起,但我自己也讨厌它们,因为我认为它们不构成或汇总得很好 - 所以我会为那个进度条显示一个更简单的替代方法,而不是believe is superior

首先是第一件事:

“所有承诺已经解决,但有些可能被拒绝”的问题通常被称为“解决方案”。我已经提供了类似问题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
});

就是这样。更清晰的代码,更多功能编程方法。无需保持状态,一切都以更具声明性的方式表达。只是我们想要完成的不是应该如何完成。