问题描述
我在网页间导航时取消我XHR请求的问题。我有一个网页,有8个请求被解雇了。我将其取消对点击当前页面以外的链接。页面档,因为它在等待下一个文件来加载。他们XHR请求显示为取消了开发工具,但新的文件档,就好像它正在等待他们回来。
在这里,您可以看到页面被冻结,即使其他所有的请求都被取消。新页面是唯一悬而未决的请求......
在这里,你可以看到,一旦页终于没有做出跳跃TTFB为52.52s。如果我等待来电回来之前马上点击跳跃是即时的。
下面是标题的新页面,一旦它最后装入有没有什么帮助?
我用下面的怪人code管理XHR请求。我对那个中止请求底部...
一个cancelAll功能 XHRManager = {
要求: [],
pendingRequests:[],
addNextRequest:函数(r)的{
变种超时= 0;
如果(trace.isDevelopment()){
超时= 350;
}
的setTimeout(函数(){
如果(r.url == XHRManager.pendingRequests [0] .URL&安培;&安培; r.start == XHRManager.pendingRequests [0]。开始){
XHRManager.pendingRequests.splice(0,1);
}
其他 {
$(XHRManager.pendingRequests)。每个(函数(II,DD){
如果(dd.url == r.url和放大器;&安培; dd.start == r.start){
XHRManager.pendingRequests.splice(二,1);
}
});
}
XHRManager.startNextRequest();
如果(trace.findLocalStorage()){
XHRManager.showTrace = TRUE;
trace.show();
}
}, 时间到);
},
要求: [],
间隔:[],
requestsInt:0,
firstRun:真正的,
延迟:500,
globalTimeout:5000,
showTrace:假的,
startNextRequest:函数(){
$(XHRManager.pendingRequests)。每个(函数(I,D){
如果(d.start){
}
如果(我== 0){
如果(trace.domWatcher.constructor ==功能){
trace.domWatcher(d.requestNumber);
}
trace.log的(请求#+ d.requestNumber +开始);
d.requestType(四);
}
});
如果(XHRManager.pendingRequests.length == 0){
如果(trace.isDevelopment()){
trace.show();
}
}
},
AddToPendingRequests:功能(URL,参数,可以CB,类型,errCB){
变种RI = XHRManager.requestsInt;
XHRManager.requestsInt ++;
VAR REQ = {网址:网址,params:一个参数,可以CB:CB,requestNumber:RI,请求类型:类型};
如果(errCB){
req.errCB = errCB;
}
XHRManager.pendingRequests.push(REQ);
//如果(trace.findLocalStorage()){
// trace.show();
//}
如果(RI == 0 || XHRManager.pendingRequests.length == 1){
XHRManager.startNextRequest();
}
},
writeVals:功能(URL,参数,可以数据,启动,CB,requestNumber){
如果($(元[内容='发展'])长度GT; 0){
尝试 {
变种响应= {};
response.requestNumber = requestNumber;
如果(data.sql =&放大器;!&安培;!data.sql =未定义){
response.sql = data.sql;
}
如果(data.debug){
如果(data.debug.sql =&放大器;!&安培;!data.debug.sql =未定义){
response.sql = data.debug.sql;
}
}
如果(数据。数据=&放大器;!&安培;!数据。数据=未定义){
response.data =数据。数据;
}
其他 {
如果(数据!=||数据!=未定义){
response.data =数据;
}
}
如果(URL =&放大器;!&安培;!URL =未定义){
response.url =网址;
}
如果(PARAMS =&放大器;!&安培;!PARAMS =未定义){
response.params = PARAMS;
}
如果(CB){
response.cb = cb.toString();
}
其他 {
response.cb =;
}
response.requestStats = {};
response.requestStats.start =启动;
response.requestStats.end =日期();
response.requestStats.totalTime =(。(新日期(response.requestStats.end))的getTime() - (新日期(开始))的getTime())/ 1000 +秒(S);
XHRManager.Requests.push(响应);
}
赶上(E){
trace.log的(E);
}
}
},
_create:函数(r)的{
VAR XM = XHRManager;
VAR开始=日期();
VAR REQ = $获得(r.url,r.params,r.cb)
.done(功能(数据){
XHRManager.writeVals(r.url,r.params,数据,启动,r.cb,r.requestNumber);
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.addNextRequest(r)的;
}
});
xm.requests.push(REQ);
},
_createAjax:函数(r)的{
VAR XM = XHRManager;
VAR开始=日期();
如果(r.type ==PUT|| r.type ==删除){
VAR REQ = $阿贾克斯({
类型:r.type,
xhrFields:{
withCredentials:真
},
网址:r.url,
数据:r.params,
成功:功能(数据){
XHRManager.writeVals(r.url,r.params,r.data,r.start,r.cb,r.requestNumber);
r.cb(数据);
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.addNextRequest(r)的;
}
},
错误:r.errCB
});
xm.requests.push(REQ);
}
其他 {
VAR REQ = $阿贾克斯({
类型:r.type,
xhrFields:{
withCredentials:真
},
数据类型:JSON,
JSON:JSON,
网址:r.url,
数据:r.params,
成功:功能(数据){
XHRManager.writeVals(r.url,r.params,数据,启动,r.cb,r.requestNumber);
r.cb(数据);
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.addNextRequest(r)的;
}
},
错误:r.errCB
});
xm.requests.push(REQ);
}
},
_createJSON:函数(r)的{
VAR开始=日期();
VAR XM = XHRManager;
VAR REQ = $ .getJSON(r.url,r.params,r.cb)
.done(功能(数据){
XHRManager.writeVals(r.url,r.params,数据,启动,r.cb,r.requestNumber);
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.addNextRequest(r)的;
}
});
xm.requests.push(REQ);
},
创建:功能(URL,参数,可以CB){
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.AddToPendingRequests(URL,参数,可以CB,XHRManager._create);
}
其他 {
变种R = {};
r.url =网址;
r.params = PARAMS;
r.cb = CB;
XHRManager._create(r)的;
}
},
createAjax:功能(URL,参数,可以键入,CB,errCB){
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.AddToPendingRequests(URL,参数,可以CB,XHRManager._createAjax,errCB);
}
其他 {
变种R = {};
r.url =网址;
r.params = PARAMS;
r.cb = CB;
r.type =类型;
r.errCB = errCB;
XHRManager._createAjax(r)的;
}
},
createJSON:功能(URL,参数,可以CB){
如果(trace.isDevelopment()&安培;&安培; trace.isOn()){
XHRManager.AddToPendingRequests(URL,参数,可以CB,XHRManager._createJSON);
}
其他 {
变种R = {};
r.url =网址;
r.params = PARAMS;
r.cb = CB;
XHRManager._createJSON(r)的;
}
},
删除:功能(XHR){
VAR XM = XHRManager;
VAR指数= xm.requests.indexOf(XHR);
如果(索引> -1){
xm.requests.splice(指数,1);
}
指数= xm.intervals.indexOf(xhr.interval);
如果(索引> -1){
xm.intervals.splice(指数,1);
}
},
cancelAll:函数(){
VAR XM = XHRManager;
$(xm.requests)。每个(函数(){
VAR T =这一点;
t.abort();
});
$(xm.intervals)。每个(函数(){
VAR T =这一点;
clearInterval(T);
});
xm.requests = [];
xm.intervals = [];
}
};
该网站使用jQuery的,PHP,Zend框架2,和SQL,Apache的。我在想什么?
可能的因果链
- 服务器没有实现XHR请求被取消,所以相应的PHP程序继续运行
- 在这些PHP程序使用会话,prevent并发访问本次会议,直到他们终止
可能的解决方案
Adressing上述两种分打破链,可以解决这个问题:
- (一)<$c$c>ignore_user_abort$c$c>是
FALSE
默认情况下,但你可以使用一个非标准的设置。更改此设置回FALSE
在你的php.ini
或呼叫<$c$c>ignore_user_abort(false)$c$c>在处理这些中断请求的脚本。
缺点:剧本只是终止。任何工作正在进行中被删除,可能导致系统处于异常状态。的
- (b)在默认情况下,PHP将无法检测到用户已经中止的连接直到试图将信息发送到客户端。做
回声
的东西定期长时间运行脚本的过程中。
缺点:这种虚拟数据可能会损坏你的脚本的正常输出。这里也一样,该脚本可以让系统处于异常状态。的
在session_start()
,该脚本将打开写模式的会议文件,有效地获取它的排他锁。使用同一会话的后续请求被搁置,直到锁被释放。这发生在脚本终止时,除非你明确地关闭会话。呼叫<$c$c>session_write_close()$c$c>或 session_abort()
早可能的。缺点:在关闭时,会话不能被写入了(除非你重新打开对话,但这有些不雅黑客攻击)。同时该脚本继续运行,可能是浪费资源。的
我绝对推荐的最后一个选项。
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...
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...
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 = [];
}
};
The site uses jQuery, PHP, Zend Framework 2, and SQL, Apache. What am I missing?
Probable causal chain
- the server does not realise the XHR requests are cancelled, and so the corresponding PHP processes keep running
- these PHP processes use sessions, and prevent concurrent access to this session until they terminate
Possible solutions
Adressing either of the above two points breaks the chain and may fix the problem:
- (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.
Drawback: the script just terminates. Any work in progress is dropped, possibly leaving the system in a dirty state.
- (b) By default, PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Do
echo
something periodically during the course of your long-running script.
Drawback: this dummy data might corrupt the normal output of your script. And here too, the script may leave the system in a dirty state.
- 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.
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.
这篇关于取消在PHP应用程序挂起的AJAX请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!