小编典典

重试一个jquery ajax请求,该请求的延迟中附加了回调

ajax

我正在尝试实现一个重试由于临时原因而失败的ajax请求的系统。在我的情况下,这是关于重试失败的请求,该请求带有401状态代码,因为会话已过期,然后调用了刷新会话的刷新Web服务。

问题在于成功完成重试后不会调用“完成”回调,这与调用“成功” ajax选项回调不同。我在下面做了一个简单的例子:

$.ajaxSetup({statusCode: {
    404: function() {
        this.url = '/existent_url';
        $.ajax(this);
    }
}});

$.ajax({
    url: '/inexistent_url',
    success: function() { alert('success'); }
})
.done(function() {
    alert('done');
});

有没有一种方法可以在成功重试后调用完成样式的回调?我知道延期后“拒绝”后就无法“解决”,是否可以防止拒绝?还是将原始延期的doneList复制到新延期?我没有主意:)

下面是一个更实际的示例,其中我试图将所有被401拒绝的请求排队,并在成功调用/ refresh之后重试它们。

var refreshRequest = null,
    waitingRequests = null;

var expiredTokenHandler = function(xhr, textStatus, errorThrown) {

    //only the first rejected request will fire up the /refresh call
    if(!refreshRequest) {
        waitingRequests = $.Deferred();
        refreshRequest = $.ajax({
            url: '/refresh',
            success: function(data) {
                // session refreshed, good
                refreshRequest = null;
                waitingRequests.resolve();
            },
            error: function(data) {
                // session can't be saved
                waitingRequests.reject();
                alert('Your session has expired. Sorry.');
            }
       });
    }

    // put the current request into the waiting queue
    (function(request) {
        waitingRequests.done(function() {
            // retry the request
            $.ajax(request);
        });
    })(this);
}

$.ajaxSetup({statusCode: {
    401: expiredTokenHandler
}});

该机制有效,第二次触发401失败的请求,问题是未调用其“完成”回调,因此应用程序停顿了。


阅读 297

收藏
2020-07-26

共1个答案

小编典典

您可以jQuery.ajaxPrefilter用来将jqXHR包装在另一个延迟的对象中

我在上面的示例jsFiddle中显示了它的工作原理,并尝试调整您的一些代码以将401处理到此版本中:

$.ajaxPrefilter(function(opts, originalOpts, jqXHR) {
    // you could pass this option in on a "retry" so that it doesn't
    // get all recursive on you.
    if (opts.refreshRequest) {
        return;
    }

    // our own deferred object to handle done/fail callbacks
    var dfd = $.Deferred();

    // if the request works, return normally
    jqXHR.done(dfd.resolve);

    // if the request fails, do something else
    // yet still resolve
    jqXHR.fail(function() {
        var args = Array.prototype.slice.call(arguments);
        if (jqXHR.status === 401) {
            $.ajax({
                url: '/refresh',
                refreshRequest: true,
                error: function() {
                    // session can't be saved
                    alert('Your session has expired. Sorry.');
                    // reject with the original 401 data
                    dfd.rejectWith(jqXHR, args);
                },
                success: function() {
                    // retry with a copied originalOpts with refreshRequest.
                    var newOpts = $.extend({}, originalOpts, {
                        refreshRequest: true
                    });
                    // pass this one on to our deferred pass or fail.
                    $.ajax(newOpts).then(dfd.resolve, dfd.reject);
                }
            });

        } else {
            dfd.rejectWith(jqXHR, args);
        }
    });

    // NOW override the jqXHR's promise functions with our deferred
    return dfd.promise(jqXHR);
});

之所以有效,是因为deferred.promise(object)它将实际上覆盖jqXHR上的所有“承诺方法”。

注意: 对于任何其他发现此问题的人,如果您在ajax选项中使用success:和附加回调error:,则此代码段将 无法
按您期望的方式工作。假定唯一的回调是使用jqXHR 的.done(callback).fail(callback)方法附加的回调。

2020-07-26