我有一个单页应用程序,它使用基于 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 请求。
我需要做什么来解决这个问题?
最佳答案
由于 jQuery 的 $.ajax
返回一个 promise (并且由于您正在使用它),请放弃使用 success
回调。而是在 then
回调中移动该代码。这将允许您链接一个 catch
方法 (jQuery 3.x) 调用它来响应错误。如果您没有在该 catch
回调中触发另一个错误,则它返回的 promise 将再次得到解析(而不是拒绝),因此链的其余部分不会被中止:
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 并非如此 Promise/A+合规性,这使得正确处理变得很痛苦。以下是如何为 jQuery 2.x 做到这一点。此代码段使用模拟延迟和 HTTP 响应状态代码的 URL,这允许它测试请求错误、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
初始化为立即解决的 Promise,您可以避免代码重复:
var appAjaxPromise = $.when();
function ajaxRequestQueue(someAjaxData, functionToCallBack, someCallBackData) {
appAjaxPromise = appAjaxPromise.then(function () {
return ajaxSender(someAjaxData, functionToCallBack, someCallBackData);
});
return appAjaxPromise;
}
关于javascript - 当成功函数失败时,jQuery Ajax promise 队列不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45536328/