当成功函数失败时,jQuery Ajax承诺队列无效

时间:2017-08-06 20:31:00

标签: javascript jquery ajax promise

我有一个单页应用程序,它使用基于promise的排队机制,如下所示:

a)处理ajax请求的函数

function AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData) {

   return $.ajax({
        url: ...,
        type: "POST",
        data: SomeAjaxData,
        success: function (msg, textStatus, request) {

            if (FunctionToCallBack) {

               FunctionToCallBack(SomeCallBackData);
               //problem if there's a bug when this executes
            }
        }
   });
}

b)使用promise对象对请求进行排队的函数

var AppAjaxPromise;    
function AjaxRequestQueue(SomeAjaxData, FunctionToCallBack, SomeCallBackData) {

        if (AppAjaxPromise) {

            AppAjaxPromise = AppAjaxPromise.then(function () {

                return AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData);
            });

            return AppAjaxPromise;
        }

        AppAjaxPromise = AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData);

        return AppAjaxPromise;
    }

当我想发送ajax请求时,我调用AjaxRequestQueue(TheAjaxData, TheFunctionToCallBack, TheCallBackData)并且排队机制确保如果同时发送多个请求,或者在一个请求完成返回之前,它们排队并在前一个请求之后处理一个完成。

当错误停止执行回调函数时,会出现问题。如果该函数出错,则整个排队机制停止,并且调用AjaxRequestQueue不再触发ajax请求。

我需要做些什么来解决这个问题?

2 个答案:

答案 0 :(得分:4)

当jQuery的$.ajax返回一个promise(并且因为你正在使用它)时,放弃使用success回调。而是在then回调中移动该代码。这将允许您链接catch方法(jQuery 3.x)调用它以响应错误。如果你没有在catch回调中触发另一个错误,它返回的承诺将再次被解决(不被拒绝),因此你的链的其余部分不会被中止:

function ajaxSender(someAjaxData, functionToCallBack, someCallBackData) {
    return $.ajax({
        url: ...,
        type: "POST",
        data: someAjaxData
    }).then(function (msg, textStatus, request) {
        if (functionToCallBack) {
           functionToCallBack(someCallBackData);
        }
    }).catch(function (err) {
        console.log('error occurred, but request queue will not be interrupted', err);
    });
}

jQuery 2.x

以上需要jQuery 3.x.在3.x之前的jQuery版本中,您可以像这样替换catch方法(注意null参数):

       ...
    }).then(null, function (err) {
       ...

...但是jQuery 2.x承诺不符合Promise/A+,这使得纠正它变得很痛苦。以下是如何为jQuery 2.x执行此操作。此代码段使用模仿延迟的URL和HTTP响应状态代码,这样可以测试请求错误,JavaScript运行时错误和排序:

function ajaxSender(someAjaxData, functionToCallBack, someCallBackData) {
    return $.ajax({
        // URL for demo: server will use the sleep parameter in the data, 
        //    and will return the given HTTP status
        url: "http://httpstat.us/" + someAjaxData.status, 
        type: "GET", // The demo URL needs a GET
        data: someAjaxData
    }).then(function (data) {
        if (functionToCallBack) {
            try { // Would not be necessary if jQuery 2.x were Promise/A+ compliant
                functionToCallBack(someCallBackData);
            } catch (e) {
                console.log(someCallBackData, 'Error occurred during callback');
            }
        }
    }, function (err) { // This second function captures ajax errors
        console.log(someCallBackData, 'HTTP error');
        // Return a resolved promise. 
        // This would not be necessary if jQuery 2.x were Promise/A+ compliant
        return $.when(); 
    }); // In jQuery 3.x you would chain a catch call here instead of the try/catch.
}

var appAjaxPromise = $.when();    
function ajaxRequestQueue(someAjaxData, functionToCallBack, someCallBackData) {
    appAjaxPromise = appAjaxPromise.then(function () {
        return ajaxSender(someAjaxData, functionToCallBack, someCallBackData);
    });
    return appAjaxPromise;
}

// Demo: the ajax data argument is also used to define the HTTP response status and  
//   the sleep time, and the data argument identifies the number of the call

// Survive an HTTP error
ajaxRequestQueue({ status: 404, sleep: 1000 }, myCallBack, 1); 
// Survive a runtime error in the callback
ajaxRequestQueue({ status: 200, sleep: 2000 }, myErrorGeneratingCallBack, 2); 
// Demo that the callback calls remain in the right order
ajaxRequestQueue({ status: 200, sleep: 3000 }, myCallBack, 3); 
ajaxRequestQueue({ status: 200, sleep: 2000 }, myCallBack, 4);
ajaxRequestQueue({ status: 200, sleep: 1000 }, myCallBack, 5); 

function myCallBack(data) {
    console.log(data, "My callback is called");
}
function myErrorGeneratingCallBack(data) {
    console.log(data, "My callback is called");
    throw "I threw an error in my callback";
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

移动到jQuery 3时,您仍然可以继续上述模式:它仍然有效。但理想情况下,您应该将代码迁移到我在顶部提供的基于catch的版本。

其他一些言论

只有当变量是构造函数/类时,才能将变量的第一个字母大写一致。

通过将appAjaxPromise初始化为立即解决的承诺,您可以避免重复代码:

var appAjaxPromise = $.when();    
function ajaxRequestQueue(someAjaxData, functionToCallBack, someCallBackData) {
    appAjaxPromise = appAjaxPromise.then(function () {
        return ajaxSender(someAjaxData, functionToCallBack, someCallBackData);
    });
    return appAjaxPromise;
}

答案 1 :(得分:0)

我不确定这个答案是否会解决它,但是你在成功函数中使用的回调可能无法从那里访问。

您可以像这样向请求中添加额外的数据......并且可以使用&#39; this ....&#39;来访问它。 (见成功)。

不确定你是否应该使用ether:p我一直这样做是为了从对象内部传递数据而不必更改ajax&#39;上下文或使用$ .proxy。此外,我还能够从请求的成功内部访问一个触发请求的对象函数,使其以块的形式发送文件时递归。

如果对此有任何意见,我很乐意听到。

return $.ajax({
    FunctionToCallBack: FunctionToCallBack,
    SomeCallBackData: SomeCallBackData,

    url: ...,
    type: "POST",
    data: SomeAjaxData,
    success: function (msg, textStatus, request) {

        if (this.FunctionToCallBack) {

           this.FunctionToCallBack(this.SomeCallBackData);
           //problem if there's a bug when this executes
        }
    }

});