jQuery Ajax 承诺队列在成功函数失败时不起作用



我有一个单页应用程序,它使用基于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( 使用承诺对象对请求进行排队的函数

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),排队机制确保如果同时发送多个请求,或者在其中一个请求完成返回之前,它们将排队并在前一个请求完成后进行处理。

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

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

由于 jQuery 的$.ajax返回了一个承诺(并且由于您正在使用它(,请放弃使用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;
}

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

您可以像这样向请求添加额外的数据...并且可以通过"这个...."访问它。(请参阅成功(。

不确定是否应该以太:p我一直在这样做是为了从对象内部传递数据,而无需更改 ajax 的上下文或使用 $.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
}
}

}(;

最新更新