jQuery - .always() 回调过早触发

jQuery - .always() callback firing too soon

本文关键字:回调 always jQuery      更新时间:2023-09-26

我正在开发一个客户端JS应用程序,该应用程序应该读取CSV文件,每行进行几次API调用,然后将结果写回CSV。我坚持的部分是如何编排请求并在所有请求完成后启动函数。这是我到目前为止所拥有的:

var requests = [];
// loop through rows
addresses.forEach(function (address, i) {
    // make request
    var addressRequest = $.ajax({
            dataType: 'json',
            url: 'http://api.com/addresses/' + address,
            success: function (data, textStatus, jqXhr) { APP.didGetAddressJson(data, i, jqXhr) },
            error: function (jqXhr, textStatus, errorThrown) { APP.didFailToGetAddressJson(errorThrown, i) },
        });
    requests.push(addressRequest);
    // make some more requests (handled by other success functions)
});
// leggo
$.when.apply($, requests).done(APP.didFinishGeocoding);

问题是,如果其中一行抛出 404,则不会调用 done 函数。我将其切换到 always 现在它被调用,但不是在最后 - 如果我将每个回调的执行记录到控制台,它通常在中间的某个地方。但是,如果我编辑 CSV 以便没有错误,它会按预期在最后调用。我在这里做什么让always提前开火的事情吗?

更新:可能只是控制台无序地登录吗?

您需要防止错误发送错误,通过$.when.apply($, requests)错误路径返回的承诺。

这可以通过以下方式实现:

  • .then()链接到$.ajax()调用,而不是将"成功"和"错误"处理程序指定为$.ajax()选项。
  • 通过转换为成功来处理错误(因为这是jQuery,你必须从错误处理程序返回一个已解决的承诺)。

此方法还允许您控制最终交付给APP.didFinishGeocoding()

通过一些假设,代码的一般形状应如下所示:

function foo () {//assume there's an outer function wrapper 
    var errorMarker = '**error**';
    var requests = addresses.map(function (address, i) {
        return $.ajax({
            dataType: 'json',
            url: 'http://api.com/addresses/' + address
        }).then(function (data, textStatus, jqXhr) { //success handler
            return APP.didGetAddressJson(data, i, jqXhr); //whatever APP.didGetAddressJson() returns will appear as a result at the next stage.
        }, function (jqXhr, textStatus, errorThrown) { // error handler
            APP.didFailToGetAddressJson(errorThrown, i);
            return $.when(errorMarker);//errorMarker will appear as a result at the next stage - but can be filtered out.
        });
        // make some more requests (handled by other success functions)
    });
    return $.when.apply($, requests).then(function() {
        //first, convert arguments to an array and filter out the errors
        var results = Array.prototype.slice.call(arguments).filter(function(r) {
            return r !== errorMarker;
        });
        //then call APP.didFinishGeocoding() with the filtered results as individual arguments.
        return APP.didFinishGeocoding.apply(APP, results);
        //alternatively, call APP.didFinishGeocoding() with the filtered results as an array.
        //return APP.didFinishGeocoding(results);
    });
}

根据需要进行调整。

尝试通过whenAll函数传递已解决的、被拒绝的 jQuery 承诺对象,在whenAll完成时过滤已解决、拒绝的 .then() 内的承诺对象。参见 Jquery Ajax 防止延迟顺序循环中的失败


(function ($) {
    $.when.all = whenAll;
    function whenAll(arr) {
        "use strict";
        var deferred = new $.Deferred(),
            args = !! arr 
                   ? $.isArray(arr) 
                     ? arr 
                     : Array.prototype.slice.call(arguments)
                       .map(function (p) {
                         return p.hasOwnProperty("promise") 
                         ? p 
                         : new $.Deferred()
                           .resolve(p, null, deferred.promise())
                       }) 
                   : [deferred.resolve(deferred.promise())],
            promises = {
                "success": [],
                  "error": []
            }, doneCallback = function (res) {
                promises[this.state() === "resolved" 
                         || res.textStatus === "success" 
                         ? "success" 
                         : "error"].push(res);
                return (promises.success.length 
                       + promises.error.length) === args.length 
                       ? deferred.resolve(promises) 
                       : res
            }, failCallback = function (res) {
                // do `error` notification , processing stuff
                // console.log(res.textStatus);
                promises[this.state() === "rejected" 
                        || res.textStatus === "error" 
                        ? "error" 
                        : "success"].push(res);
                return (promises.success.length 
                       + promises.error.length) === args.length 
                       ? deferred.resolve(promises) 
                       : res
            };
        $.map(args, function (promise, index) {
            return $.when(promise).always(function (data, textStatus, jqxhr) {
                return (textStatus === "success") 
                    ? doneCallback.call(jqxhr, {
                        data: data,
                        textStatus: textStatus 
                                    ? textStatus 
                                    : jqxhr.state() === "resolved" 
                                      ? "success" 
                                      : "error",
                        jqxhr: jqxhr
                      }) 
                    : failCallback.call(data, {
                        data: data,
                        textStatus: textStatus,
                        jqxhr: jqxhr
                      })
            })
        });
        return deferred.promise()
    };
}(jQuery));

例如

var request = function (url) {
    return $.ajax({
                   url: "http://api.com/addresses/" + url, 
                   dataType: "json"
           })
    }
, addresses = [
    ["/echo/json/"], // `success`
    ["/echo/jsons/"], // `error`
    ["/echo/json/"], // `success`
    ["/echo/jsons/"], // `error`
    ["/echo/json/"] // `success`
];
$.when.all(
  $.map(addresses, function (address) {
    return request(address)
  })
)
.then(function (data) {
    console.log(data);
    // filter , process responses
    $.each(data, function(key, value) {
        if (key === "success") {
           value.forEach(function(success, i) {
              console.log(success, i);
              APP.didGetAddressJson(success.data, i, success.jqxhr);
            })
        } else {            
           value.forEach(function(error, i) {
              console.log(error, i);
              APP.didFailToGetAddressJson(error.jqxhr, i)
          })
        }
    })
}, function (e) {
    console.log("error", e)
});

JSFIDDLE http://jsfiddle.net/guest271314/ev4urod1/