继续jQuery延迟链的失败

时间:2015-10-20 21:08:40

标签: javascript jquery jquery-deferred deferred

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

$.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?似乎任何延迟的失败都会导致链的其余部分也失败。相反,我想记录错误并继续列表的其余部分。

2 个答案:

答案 0 :(得分:5)

使用Promises/A+,这就像

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

不幸的是,jQuery is still not Promises/A+ compliant并转发旧值(结果或错误) - 除非您从回调中返回一个延迟的jQuery。这与rejecting from the success handler的工作原理相同:

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

答案 1 :(得分:1)

从jQuery promise链中的错误中恢复比使用Promise / A +实现更加冗长,Promise / A +实现自然会捕获.catch或.then的错误处理程序中的错误。您必须抛出/重新抛出才能传播错误状态。

jQuery以相反的方式工作。一个.then的错误处理程序(.catch不存在)将自然地传播错误状态。要模仿" catch",您必须返回已解决的承诺,并且链将沿着其成功路径前进。

从您希望基于一系列异步调用的项目数组开始,使用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。