取消 PHP 应用程序中未决的 AJAX 请求?
在页面之间导航时取消我的 XHR 请求时遇到问题.我有一个页面有 8 个请求被触发.我在单击当前页面之外的链接时取消它们.页面在等待下一个要加载的文档时停止.他们的 XHR 请求在开发人员工具中显示为已取消,但新文档停滞不前,好像在等待他们回来一样.
I'm having problems canceling my XHR requests when navigating between pages. I have a page that has 8 requests that get fired off. I cancel them on click of a link outside of the current page. The page stalls as it waits on the next document to load. They XHR requests appear as cancelled in developer tools, but the new document stalls as if it is waiting for them to come back.
在此处您可以看到页面已停止,即使所有其他请求都已取消.新页面是唯一待处理的请求...
Here you can see the page is stalled even though all the other requests are cancelled. The new page is the only pending request...
在这里您可以看到,一旦页面最终跳转,TTFB 为 52.52 秒.如果我在点击离开之前等待电话回来,那么跳跃是即时的.
And here you can see once the page finally did make the jump the TTFB is 52.52s. If I wait for the calls to come back before clicking away the jump is instant.
如果有帮助,这里是新页面最终加载后的标题...
Here are the headers for the new pages once it finally loads if that helps...
我使用以下 frankenstein 代码来管理 XHR 请求.我在底部有一个取消请求的 cancelAll 函数......
I use the following frankenstein code to manage XHR requests. I have a cancelAll function towards the bottom that aborts the requests...
XHRManager = {
Requests: [],
pendingRequests: [],
addNextRequest: function (r) {
var timeout = 0;
if (trace.isDevelopment()) {
timeout = 350;
}
setTimeout(function () {
if (r.url == XHRManager.pendingRequests[0].url && r.start == XHRManager.pendingRequests[0].start) {
XHRManager.pendingRequests.splice(0, 1);
}
else {
$( XHRManager.pendingRequests).each(function (ii, dd) {
if (dd.url == r.url && dd.start == r.start) {
XHRManager.pendingRequests.splice(ii, 1);
}
});
}
XHRManager.startNextRequest();
if (trace.findLocalStorage()) {
XHRManager.showTrace = true;
trace.show();
}
}, timeout);
},
requests: [],
intervals: [],
requestsInt: 0,
firstRun: true,
delay: 500,
globalTimeout: 5000,
showTrace: false,
startNextRequest: function () {
$( XHRManager.pendingRequests).each(function (i, d) {
if (d.start) {
}
if (i == 0) {
if (trace.domWatcher.constructor == Function) {
trace.domWatcher(d.requestNumber);
}
trace.log("Request #" + d.requestNumber + " started");
d.requestType(d);
}
});
if ( XHRManager.pendingRequests.length == 0) {
if (trace.isDevelopment()) {
trace.show();
}
}
},
AddToPendingRequests: function (url, params, cb, type, errCB) {
var rI = XHRManager.requestsInt;
XHRManager.requestsInt++;
var req = {url: url, params: params, cb: cb, requestNumber: rI, requestType: type};
if (errCB) {
req.errCB = errCB;
}
XHRManager.pendingRequests.push(req);
// if(trace.findLocalStorage()){
// trace.show();
// }
if (rI == 0 || XHRManager.pendingRequests.length == 1) {
XHRManager.startNextRequest();
}
},
writeVals: function (url, params, data, start, cb, requestNumber) {
if ($("meta[content='development']").length > 0) {
try {
var response = {};
response.requestNumber = requestNumber;
if (data.sql != "" && data.sql != undefined) {
response.sql = data.sql;
}
if (data.debug) {
if (data.debug.sql != "" && data.debug.sql != undefined) {
response.sql = data.debug.sql;
}
}
if (data.data != "" && data.data != undefined) {
response.data = data.data;
}
else {
if (data != "" || data != undefined) {
response.data = data;
}
}
if (url != "" && url != undefined) {
response.url = url;
}
if (params != "" && params != undefined) {
response.params = params;
}
if (cb) {
response.cb = cb.toString();
}
else {
response.cb = "";
}
response.requestStats = {};
response.requestStats.start = start;
response.requestStats.end = Date();
response.requestStats.totalTime = ((new Date(response.requestStats.end)).getTime() - (new Date(start)).getTime()) / 1000 + " sec(s)";
XHRManager.Requests.push(response);
}
catch (e) {
trace.log(e);
}
}
},
_create: function (r) {
var xm = XHRManager;
var start = Date();
var req = $.get(r.url, r.params, r.cb)
.done(function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
});
xm.requests.push(req);
},
_createAjax: function (r) {
var xm = XHRManager;
var start = Date();
if (r.type == "PUT" || r.type == "DELETE") {
var req = $.ajax({
type: r.type,
xhrFields: {
withCredentials: true
},
url: r.url,
data: r.params,
success: function (data) {
XHRManager.writeVals(r.url, r.params, r.data, r.start, r.cb, r.requestNumber);
r.cb(data);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
},
error: r.errCB
});
xm.requests.push(req);
}
else {
var req = $.ajax({
type: r.type,
xhrFields: {
withCredentials: true
},
dataType: 'json',
json: 'json',
url: r.url,
data: r.params,
success: function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
r.cb(data);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
},
error: r.errCB
});
xm.requests.push(req);
}
},
_createJSON: function (r) {
var start = Date();
var xm = XHRManager;
var req = $.getJSON(r.url, r.params, r.cb)
.done(function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
});
xm.requests.push(req);
},
create: function (url, params, cb) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._create);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
XHRManager._create(r);
}
},
createAjax: function (url, params, type, cb, errCB) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._createAjax, errCB);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
r.type = type;
r.errCB = errCB;
XHRManager._createAjax(r);
}
},
createJSON: function (url, params, cb) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._createJSON);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
XHRManager._createJSON(r);
}
},
remove: function (xhr) {
var xm = XHRManager;
var index = xm.requests.indexOf(xhr);
if (index > -1) {
xm.requests.splice(index, 1);
}
index = xm.intervals.indexOf(xhr.interval);
if (index > -1) {
xm.intervals.splice(index, 1);
}
},
cancelAll: function () {
var xm = XHRManager;
$(xm.requests).each(function () {
var t = this;
t.abort();
});
$(xm.intervals).each(function () {
var t = this;
clearInterval(t);
});
xm.requests = [];
xm.intervals = [];
}
};
该站点使用 jQuery、PHP、Zend Framework 2 和 SQL、Apache.我错过了什么?
The site uses jQuery, PHP, Zend Framework 2, and SQL, Apache. What am I missing?
推荐答案
可能的因果链
- 服务器没有意识到XHR请求被取消,所以相应的PHP进程继续运行
- 这些 PHP 进程使用会话,并阻止并发访问此会话,直到它们终止
可能的解决方案
解决以上两点中的任何一个都会破坏链条并可能解决问题:
Possible solutions
Adressing either of the above two points breaks the chain and may fix the problem:
- (a)
ignore_user_abort
默认为FALSE
,但您可以使用非标准设置.在php.ini
中将此设置改回FALSE
或调用ignore_user_abort(false)
在处理这些可中断请求的脚本中.
- (a)
ignore_user_abort
isFALSE
by default, but you could be using a non-standard setting. Change this setting back toFALSE
in youphp.ini
or callignore_user_abort(false)
in the scripts that handle these interruptible requests.
缺点:脚本刚刚终止.任何正在进行的工作都会被丢弃,可能会使系统处于脏状态.
- (b) 默认情况下,PHP 不会检测到用户已中止连接,直到尝试向客户端发送信息.在长时间运行的脚本过程中定期执行
echo
操作.
缺点:这些虚拟数据可能会破坏脚本的正常输出.在这里,脚本也可能使系统处于肮脏状态.
- PHP 会话作为文件存储在服务器上.在
session_start()
上,脚本以写入模式打开会话文件,有效地获取了一个独占锁.使用同一会话的后续请求将被搁置,直到释放锁为止.当脚本终止时会发生这种情况,除非您明确关闭会话.调用session_write_close()
或session_abort()
越早越好.
- A PHP sessions is stored as a file on the server. On
session_start()
, the script opens the session file in write mode, effectively acquiring an exclusive lock on it. Subsequent requests that use the same session are put on hold until the lock is released. This happens when the script terminates, unless you close the session explicitely. Callsession_write_close()
orsession_abort()
as early as possible.
缺点:关闭后,无法再写入会话(除非您重新打开会话,但这有点不雅的hack).脚本也会继续运行,可能会浪费资源.
Drawback: when closed, the session cannot be written anymore (unless you reopen the session, but this is somewhat inelegant a hack). Also the script does keep running, possibly wasting resources.
我绝对推荐最后一个选项.
I definitely recommend the last option.
相关文章