jQuery 延迟链失败继续

Continue of failure of jQuery Deferred chain

我正在 jQuery 中执行一系列顺序 AJAX 调用,使用通常的链接方法与 Deferred。第一次调用 returns 一个值列表,随后的调用是使用这些返回的列表条目进行的。在第一次调用 returns 列表后,后续调用可以按任何顺序进行,但必须一次进行一次。所以这就是我使用的:

$.when(callWebService()).then(
function (data) {
    var looper = $.Deferred().resolve(),
        myList = JSON.parse(data);
    for (var i in myList) {
        (function (i) {
            looper = looper.then(function () { // Success
                return callWebService();
            }, 
            function (jqXHR, textStatus, errorThrown) { // Failure
                if (checkIfContinuable(errorThrown) == true)
                    continueChain();
                else
                    failWithTerribleError();
            });
        })(i);
    }
});

原来后面的调用有时会失败,但我还是想做剩下的调用。在我的列表中,这就是这段创造性伪代码的目的:

if (checkIfContinuable(errorThrown) == true)
    continueChain();
else
    failWithTerribleError();

不过,我究竟该如何实施 continueChain?似乎任何延迟的失败都会导致链的其余部分也失败。相反,我想记录错误并继续列表的其余部分。

使用 Promises/A+ 这就像

一样简单
promise.then(…, function(err) {
    if (checkIfContinuable(err))
        return valueToConinueWith;
    else
        throw new TerribleError(err);
})

不幸的是,jQuery is still not Promises/A+ compliant, and forwards the old value (result or error) - unless you return a jQuery Deferred from the callback. This works just the same way as rejecting from the success handler

jDeferred.then(…, function(err) {
    if (checkIfContinuable(err))
        return $.Deferred().resolve(valueToConinueWith);
    else
        return $.Deferred().reject(new TerribleError(err));
})

从 jQuery 承诺链中的错误中恢复比使用 Promises/A+ 实现更冗长,后者自然会在 .catch 或 .then 的错误处理程序中捕获错误。您必须 throw/rethrow 才能传播错误状态。

jQuery 反过来。 .then 的错误处理程序(.catch 不存在)自然会传播错误状态。要效仿 "catch",您必须 return 一个已解决的承诺,链将沿着它的成功之路前进。

从您希望作为一系列异步调用基础的项目数组开始,使用 Array.prototype.reduce() 很方便。

function getWebServiceResults() {
    return callWebService().then(function(data) {
        var myList;

        // This is genuine Javascript try/catch, in case JSON.parse() throws.
        try {
            myList = JSON.parse(data);
        }
        catch (error) {
            return $.Deferred().reject(error).promise();//must return a promise because that's what the caller expects, whatever happens.
        }

        //Now use `myList.reduce()` to build a promise chain from the array `myList` and the items it contains.
        var promise = myList.reduce(function(promise, item) {
            return promise.then(function(arr) {
                return callWebService(item).then(function(result) {
                    arr.push(result);
                    return arr;
                }, function(jqXHR, textStatus, errorThrown) {
                    if(checkIfContinuable(errorThrown)) {
                        return $.when(arr); // return a resolved jQuery promise to put promise chain back on the success path.
                    } else {
                        return new Error(textStatus);//Although the error state will be naturally propagated, it's generally better to pass on a single js Error object rather than the three-part jqXHR, textStatus, errorThrown set.
                    }
                });
            });
        }, $.when([])) // starter promise for the reduction, resolved with an empty array

        // At this point, `promise` is a promise of an array of results.

        return promise.then(null, failWithTerribleError);
    });
}

备注:

  • 假定一个整体函数包装器,function getWebServiceResults() {...}
  • callWebService() 假定接受 item - 即 myList.
  • 的每个元素的内容
  • 要完成它的工作,checkIfContinuable() 必须接受至少一个参数。假定接受 errorThrown 但可能同样接受 jqXHR 或 textStatus.