PDA

View Full Version : [2.0rc1] Queue for concurrent Ajax calls



ThorstenSuckow
26 Nov 2007, 4:16 AM
Edit 12-October-2009

Code for Ext 3.0.2



/**
* Overrides ext-adapter behavior for allowing queuing of AJAX requests.
*
*/
/*
* Portions of this code are based on pieces of Yahoo User Interface Library
* Copyright (c) 2007, Yahoo! Inc. All rights reserved.
* YUI licensed under the BSD License:
* http://developer.yahoo.net/yui/license.txt
*/
Ext.lib.Ajax = function() {

/**
* @type {Array} _queue A FIFO queue for processing pending requests
*/
var _queue = [];

/**
* @type {Number} _activeRequests The number of requests currently being processed.
*/
var _activeRequests = 0;

/**
* @type {Number} _concurrentRequests The number of max. concurrent requests requests allowed.
*/
var _concurrentRequests = 2;

switch (true) {
case Ext.isIE8:
_concurrentRequests = window.maxConnectionsPerServer;
break;
case Ext.isIE:
_concurrentRequests = 2;
break;
case Ext.isSafari:
case Ext.isChrome:
case Ext.isGecko3:
_concurrentRequests = 4;
break;
}

var activeX = [
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP'
], CONTENTTYPE = 'Content-Type';

// private
function setHeader(o)
{
var conn = o.conn, prop;

function setTheHeaders(conn, headers) {
for (prop in headers) {
if (headers.hasOwnProperty(prop)) {
conn.setRequestHeader(prop, headers[prop]);
}
}
}

if (pub.defaultHeaders) {
setTheHeaders(conn, pub.defaultHeaders);
}

if (pub.headers) {
setTheHeaders(conn, pub.headers);
delete pub.headers;
}
}

// private
function createExceptionObject(tId, callbackArg, isAbort, isTimeout)
{
return {
tId : tId,
status : isAbort ? -1 : 0,
statusText : isAbort ? 'transaction aborted' : 'communication failure',
isAbort: isAbort,
isTimeout: isTimeout,
argument : callbackArg
};
}

// private
function initHeader(label, value)
{
(pub.headers = pub.headers || {})[label] = value;
}

// private
function createResponseObject(o, callbackArg)
{
var headerObj = {},
headerStr,
conn = o.conn,
t,
s;

try {
headerStr = o.conn.getAllResponseHeaders();
Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){
t = v.indexOf(':');
if(t >= 0){
s = v.substr(0, t).toLowerCase();
if(v.charAt(t + 1) == ' '){
++t;
}
headerObj[s] = v.substr(t + 1);
}
});
} catch(e) {}

return {
tId : o.tId,
status : conn.status,
statusText : conn.statusText,
getResponseHeader : function(header){return headerObj[header.toLowerCase()];},
getAllResponseHeaders : function(){return headerStr},
responseText : conn.responseText,
responseXML : conn.responseXML,
argument : callbackArg
};
}

// private
function releaseObject(o)
{
//console.log(o.tId+" releasing");
_activeRequests--;

o.conn = null;
o = null;

_processQueue();
}

// private
function handleTransactionResponse(o, callback, isAbort, isTimeout)
{
if (!callback) {
releaseObject(o);
return;
}

var httpStatus, responseObject;

try {
if (o.conn.status !== undefined && o.conn.status != 0) {
httpStatus = o.conn.status;
}
else {
httpStatus = 13030;
}
}
catch(e) {
httpStatus = 13030;
}

if ((httpStatus >= 200 && httpStatus < 300) || (Ext.isIE && httpStatus == 1223)) {
responseObject = createResponseObject(o, callback.argument);
if (callback.success) {
if (!callback.scope) {
callback.success(responseObject);
}
else {
callback.success.apply(callback.scope, [responseObject]);
}
}
}
else {
switch (httpStatus) {
case 12002:
case 12029:
case 12030:
case 12031:
case 12152:
case 13030:
responseObject = createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false), isTimeout);
if (callback.failure) {
if (!callback.scope) {
callback.failure(responseObject);
}
else {
callback.failure.apply(callback.scope, [responseObject]);
}
}
break;

default:
responseObject = createResponseObject(o, callback.argument);
if (callback.failure) {
if (!callback.scope) {
callback.failure(responseObject);
}
else {
callback.failure.apply(callback.scope, [responseObject]);
}
}
}
}

releaseObject(o);
responseObject = null;
}

// private
function handleReadyState(o, callback)
{
callback = callback || {};
var conn = o.conn,
tId = o.tId,
poll = pub.poll,
cbTimeout = callback.timeout || null;

if (cbTimeout) {
pub.timeout[tId] = setTimeout(function() {
pub.abort(o, callback, true);
}, cbTimeout);
}

poll[tId] = setInterval(
function() {
if (conn && conn.readyState == 4) {
clearInterval(poll[tId]);
poll[tId] = null;

if (cbTimeout) {
clearTimeout(pub.timeout[tId]);
pub.timeout[tId] = null;
}

handleTransactionResponse(o, callback);
}
},
pub.pollInterval);
}

/**
* Pushes the request into the queue if a connection object can be created
* na dimmediately processes the queue.
*
*/
function asyncRequest(method, uri, callback, postData)
{
var o = getConnectionObject();

if (!o) {
return null;
} else {
_queue.push({
o : o,
method : method,
uri : uri,
callback : callback,
postData : postData
});
//console.log(o.tId+" was put into the queue");
var head = _processQueue();

if (head) {
//console.log(o.tId+" is being processed a the head of queue");
return head;
} else {
//console.log(o.tId+" was put into the queue and will be processed later on");
return o;
}
}
}

/**
* Initiates the async request and returns the request that was created,
* if, and only if the number of currently active requests is less than the number of
* concurrent requests.
*/
function _processQueue()
{
var to = _queue[0];
if (to && _activeRequests < _concurrentRequests) {
to = _queue.shift();
_activeRequests++;
return _asyncRequest(to.method, to.uri, to.callback, to.postData, to.o);
}
}


// private
function _asyncRequest(method, uri, callback, postData, o)
{
if (o) {
o.conn.open(method, uri, true);

if (pub.useDefaultXhrHeader) {
initHeader('X-Requested-With', pub.defaultXhrHeader);
}

if(postData && pub.useDefaultHeader && (!pub.headers || !pub.headers[CONTENTTYPE])){
initHeader(CONTENTTYPE, pub.defaultPostHeader);
}

if (pub.defaultHeaders || pub.headers) {
setHeader(o);
}

handleReadyState(o, callback);
o.conn.send(postData || null);
}
return o;
}

// private
function getConnectionObject()
{
var o;

try {
//console.log(pub.transactionId+" is the current transaction id");
if (o = createXhrObject(pub.transactionId)) {
pub.transactionId++;
}
} catch(e) {
} finally {
return o;
}
}

// private
function createXhrObject(transactionId)
{
var http;

try {
http = new XMLHttpRequest();
} catch(e) {
for (var i = 0; i < activeX.length; ++i) {
try {
http = new ActiveXObject(activeX[i]);
break;
} catch(e) {}
}
} finally {
return {conn : http, tId : transactionId};
}
}

var pub = {

request : function(method, uri, cb, data, options) {
if(options){
var me = this,
xmlData = options.xmlData,
jsonData = options.jsonData,
hs;

Ext.applyIf(me, options);

if(xmlData || jsonData){
hs = me.headers;
if(!hs || !hs[CONTENTTYPE]){
initHeader(CONTENTTYPE, xmlData ? 'text/xml' : 'application/json');
}
data = xmlData || (Ext.isObject(jsonData) ? Ext.encode(jsonData) : jsonData);
}
}
return asyncRequest(method || options.method || "POST", uri, cb, data);
},

serializeForm : function(form)
{
var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
hasSubmit = false,
encoder = encodeURIComponent,
element,
options,
name,
val,
data = '',
type;

Ext.each(fElements, function(element) {
name = element.name;
type = element.type;

if (!element.disabled && name){
if(/select-(one|multiple)/i.test(type)){
Ext.each(element.options, function(opt) {
if (opt.selected) {
data += String.format("{0}={1}&",
encoder(name),
encoder((opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttribute('value') !== null) ? opt.value : opt.text));
}
});
} else if(!/file|undefined|reset|button/i.test(type)) {
if(!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)){

data += encoder(name) + '=' + encoder(element.value) + '&';
hasSubmit = /submit/i.test(type);
}
}
}
});
return data.substr(0, data.length - 1);
},

useDefaultHeader : true,
defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
useDefaultXhrHeader : true,
defaultXhrHeader : 'XMLHttpRequest',
poll : {},
timeout : {},
pollInterval : 50,
transactionId : 0,


abort : function(o, callback, isTimeout)
{
var me = this,
tId = o.tId,
isAbort = false;

//console.log(o.tId+" is aborting - was "+o.tId+" in progress?: "+me.isCallInProgress(o));

if (me.isCallInProgress(o)) {
o.conn.abort();
clearInterval(me.poll[tId]);
me.poll[tId] = null;

clearTimeout(pub.timeout[tId]);
me.timeout[tId] = null;

// @ext-bug 3.0.2 why was this commented out? if the request is aborted
// programmatically, the timeout for the "timeout"-handler is never destroyed,
// thus this method would at least be called once, if the initial reason is
// that no timeout occured.
//if (isTimeout) {
// me.timeout[tId] = null;
//}

handleTransactionResponse(o, callback, (isAbort = true), isTimeout);
} else {
// check here if the current call was in progress. This might not be the case
// if the connection was put into the queue, waiting to get triggered
for (var i = 0, max_i = _queue.length; i < max_i; i++) {
if (_queue[i].o.tId == o.tId) {
_queue.splice(i, 1);
//console.log(o.tId+" was not a call in progress, thus removed from the queue at "+i);
break;
}
}

}

return isAbort;
},

isCallInProgress : function(o) {
// if there is a connection and readyState is not 0 or 4
return o.conn && !{0:true,4:true}[o.conn.readyState];
}
};
return pub;
}();


-------

I have this application where a huge amount of server interaction is going on (update views, send data, periodically update data etc.).
There may be cases when more than 2 ajax calls are being made to the server. The Internet Explorer, for example, states itself as standard compliant and does not allow to open more than 2 simultaneously connections from the browser to the server (that goes for HTTP1.1. Using HTTP1.0, IE will allow 4 concurrent connections) - thus, any previously made XMLHttpRequest may be cancelled without even triggering some sort of exception, AFAIK. Mozilla Firefox does indeed allow more than 2 concurrent connections being made to the server, but still, clients do want to use the browser they are used to, and in the end, there is no way to predict that there are never more than - let's say - 4 requests.

Anyway, I have extended Ext.lib.Ajax (ext adapter) to put requests into a queue and then processes it using the FIFO prinicple. It guarantees that there will be never more than 2 connections opened from the client's browser to the server at one time. Once a request is finished, the implementation will peek into the queue and look if there are still requests pending, and process them subsequently.




/**
* The queue that will store all XMLHttpRequests
*/
Ext.lib.Ajax._queue = [];

/**
* Stores the number of XMLHttpRequests being processed
*/
Ext.lib.Ajax._activeRequests = 0;

/**
* Overwritten so pending XMLHttpRequests in the queue will be removed
*/
Ext.lib.Ajax.abort=function(o, callback, isTimeout)
{
if (this.isCallInProgress(o)) {
o.conn.abort();
window.clearInterval(this.poll[o.tId]);
delete this.poll[o.tId];
if (isTimeout) {
delete this.timeout[o.tId];
}

this.handleTransactionResponse(o, callback, true);

return true;
}
else {

// check if the connection is pending and delete it
for (var i = 0, max_i = this._queue.length; i < max_i; i++) {
if (this._queue[i].o.tId == o.tId) {
this._queue.splice(i, 1);
break;
}
}

return false;
}
};

/**
* Pushes the XMLHttpRequests into the queue and processes the queue afterwards.
*
*/
Ext.lib.Ajax.asyncRequest = function(method, uri, callback, postData)
{
var o = this.getConnectionObject();

if (!o) {
return null;
}
else {

this._queue.push({
o : o,
method: method,
uri: uri,
callback: callback,
postData : postData
});

this._processQueue();

return o;
}
};

/**
* Peeks into the queue and will process the first XMLHttpRequest found, if, and only if
* there are not more than 2 simultaneously XMLHttpRequests already processing.
*/
Ext.lib.Ajax._processQueue = function()
{
var to = this._queue[0];

if (to && this._activeRequests < 2) {
to = this._queue.shift();
this._asyncRequest(to.o, to.method, to.uri, to.callback, to.postData);
}

};

/**
* Executes a XMLHttpRequest and updates the _activeRequests property to match the
* number of concurrent ajax calls.
*/
Ext.lib.Ajax._asyncRequest = function(o, method, uri, callback, postData)
{
this._activeRequests++;
o.conn.open(method, uri, true);

if (this.useDefaultXhrHeader) {
if (!this.defaultHeaders['X-Requested-With']) {
this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
}
}

if(postData && this.useDefaultHeader){
this.initHeader('Content-Type', this.defaultPostHeader);
}

if (this.hasDefaultHeaders || this.hasHeaders) {
this.setHeader(o);
}

this.handleReadyState(o, callback);
o.conn.send(postData || null);

};

/**
* Called after a XMLHttpRequest finishes. Updates the number of ongoing ajax calls
* and checks afterwards if there are still requests pending.
*/
Ext.lib.Ajax.releaseObject = function(o)
{
o.conn = null;
o = null;

this._activeRequests--;
this._processQueue();
};

DragonFist
26 Nov 2007, 6:35 AM
Looks, nice. I'll check this out latter and give any feedback needed.

devnull
27 Nov 2007, 8:46 AM
hmm, thats interesting about IE. a browser should *never* silently drop connections, but if anyone is gonna do something like that, trust MS. that said, I have an app that definently makes more than 2 concurrent connections and it works fine in ie (6 and 7).

DragonFist
27 Nov 2007, 12:09 PM
Definitely the MS ajax library does this. I haven't had any trouble since I dropped using it as a way to connect to webservices (use slightly altered Ext.Ajax, Treeloader, etc. for that now).

wm003
27 Nov 2007, 12:20 PM
Very good idea and fine code as always :)

dawesi
13 Dec 2007, 8:39 PM
can multiple queues be setup for various reasons?

ie: you have two components that contstantly talk to the server, so you have three queues, one for component one, one for component two and one for all other components?

ie: specify a queue as a parameter?

hakunin
28 Feb 2008, 1:02 PM
Its awesome! An absolute must have if you send events fired by user. (chat, online gaming, interactive gui)

Thank you very much.

MaximGB
28 Feb 2008, 1:26 PM
Hm, this should be proposed as the default behavior I think.

hakunin
29 Feb 2008, 4:11 AM
can multiple queues be setup for various reasons?

ie: you have two components that constantly talk to the server, so you have three queues, one for component one, one for component two and one for all other components?

ie: specify a queue as a parameter?

You're right. There has to be more queues. For example:

I have listening thread which waits for events from server and one "slot" for thread that sends requests from user.

What happens when the user goes crazy and fires 10 events in a second? (my example = 10 messages in a chat room at once)

1) All 10 requests get into queue. First one triggers an event and listening request recieves that event. Both close.

2) Closing listening request makes another request for listening. That request is the last one in queue.

2) Next 8 crazy fired requests are sent as well as other. The response to them comes after all the crazy fired events from user are sent.


Now in case any of these crazy fired requests from user are slow the response gets ultra slow. We'd certainly get better behaviour if there were two queues.

If my example isn't clear to anyone please tell me. I'll try to explain it better.

jsakalos
18 Mar 2008, 6:11 AM
Thank you for this override.

I've been using it for months and it runs w/o any problems so I can only recommend it to others.

=D>=D>=D>

malraux
6 May 2008, 8:25 AM
Is this actually true?

I created a test to see, with a server function that takes 5 seconds to return.

I could stack as many ajax requests as I pleased, and IE 6, IE 7, and Firefox 2 all queued the requests properly without using this extension.

How did you test this?

Thanks.

ThorstenSuckow
6 May 2008, 8:35 AM
Actually, it's in the RFCs and they suggest to never open more than 2 concurrent connections from one client to one and the same host.

FF has a limit of 4, IE7 has a limit of 2, and IE8 will have a limit of 6.

Try open lots of concurrent connections to a specific url on your host - the requests get send to the server, but depending on your browser's default limit, you'll never get a proper response (you can check the responds using FF with firebug).

malraux
6 May 2008, 8:40 AM
That's exactly what I did, and as I said, I had no problems with multiple concurrent connections in any of the browsers. The browsers queued the connections and sent them when previously open connections returned after the 5-second server sleep:



Connection #1 INIT at Tue May 6 11:12:34 EST 2008
Connection #2 INIT at Tue May 6 11:12:35 EST 2008
Connection #3 INIT at Tue May 6 11:12:36 EST 2008
Connection #4 INIT at Tue May 6 11:12:36 EST 2008
Connection #5 INIT at Tue May 6 11:12:36 EST 2008
Connection #6 INIT at Tue May 6 11:12:37 EST 2008
Connection #7 INIT at Tue May 6 11:12:37 EST 2008
Connection #8 INIT at Tue May 6 11:12:38 EST 2008
Connection #9 INIT at Tue May 6 11:12:38 EST 2008
Connection #10 INIT at Tue May 6 11:12:38 EST 2008
Connection #1 RETN at Tue May 6 11:12:39 EST 2008 : true
Connection #2 RETN at Tue May 6 11:12:40 EST 2008 : true
Connection #3 RETN at Tue May 6 11:12:45 EST 2008 : true
Connection #4 RETN at Tue May 6 11:12:45 EST 2008 : true
Connection #5 RETN at Tue May 6 11:12:50 EST 2008 : true
Connection #6 RETN at Tue May 6 11:12:50 EST 2008 : true
Connection #7 RETN at Tue May 6 11:12:55 EST 2008 : true
Connection #8 RETN at Tue May 6 11:12:55 EST 2008 : true
Connection #9 RETN at Tue May 6 11:12:59 EST 2008 : true
Connection #10 RETN at Tue May 6 11:13:00 EST 2008 : true


Incidentally, FF, IE 6, and IE 7 all showed a 2 connection HTTP 1.1 limit.

malraux
6 May 2008, 8:44 AM
Here's the client code:



Ext.onReady(function(){
var num = 1;

var simple = new Ext.Panel({
frame:true,
title: 'Connection Limit Test',
width: 550,

tbar: [{
text: 'Test',
handler: function()
{
Ext.DomHelper.append(simple.body, "Connection #" + num + " INIT at " + (new Date()) + "<br>");
Ext.Ajax.request({
url: "/servlet/longpoll/wait.json",
callback: function(options, success) {
Ext.DomHelper.append(simple.body, "Connection #" + options.num + " RETN at " + (new Date()) + " : " + success + "<br>");
}, num: num++
});
}
}]
});

simple.render(document.body);
});


And the server code (not that it matters much, as the important part is the sleep :-) :



public ModelAndView wait(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e)
{
ServletUtil.addError(request, e.getMessage());
}
return new ModelAndView(new JsonView());
}

hendricd
6 May 2008, 8:51 AM
Curious, what about this way?


handler: function()
{
while (num < 15){
Ext.DomHelper.append(simple.body, "Connection #" + num + " INIT at " + (new Date()) + "<br>");
Ext.Ajax.request({
url: "/servlet/longpoll/wait.json",
callback: function(options, success) {
Ext.DomHelper.append(simple.body, "Connection #" + options.num + " RETN at " + (new Date()) + " : " + success + "<br>");
}, num: num++
});
}
}

malraux
6 May 2008, 10:27 AM
Good idea, and very interesting results.

Firefox appears to have an internal queue of size 12. IE appears to have an internal queue of size 10. Both IE6 and IE7 reacted the same way.

I modified the code to pass the number as a parameter and to echo it back in the JSON return, and to allow multiple button presses. Then I pressed it once, waited for 6 calls to return, then pressed it again.

FF:

Connection #1 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #2 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #3 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #4 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #5 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #6 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #7 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #8 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #9 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #10 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #11 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #12 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #13 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #14 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #15 INIT at Tue May 06 2008 14:23:39 GMT-0400 (EDT)
Connection #1 RETN at Tue May 06 2008 14:23:44 GMT-0400 (EDT) : true ({"num":"1","success":true,"errors":null})
Connection #2 RETN at Tue May 06 2008 14:23:44 GMT-0400 (EDT) : true ({"num":"2","success":true,"errors":null})
Connection #3 RETN at Tue May 06 2008 14:23:49 GMT-0400 (EDT) : true ({"num":"3","success":true,"errors":null})
Connection #4 RETN at Tue May 06 2008 14:23:49 GMT-0400 (EDT) : true ({"num":"4","success":true,"errors":null})
Connection #5 RETN at Tue May 06 2008 14:23:54 GMT-0400 (EDT) : true ({"num":"5","success":true,"errors":null})
Connection #6 RETN at Tue May 06 2008 14:23:54 GMT-0400 (EDT) : true ({"num":"6","success":true,"errors":null})
Connection #16 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #17 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #18 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #19 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #20 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #21 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #22 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #23 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #24 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #25 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #26 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #27 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #28 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #29 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #30 INIT at Tue May 06 2008 14:23:55 GMT-0400 (EDT)
Connection #7 RETN at Tue May 06 2008 14:23:59 GMT-0400 (EDT) : true ({"num":"7","success":true,"errors":null})
Connection #8 RETN at Tue May 06 2008 14:23:59 GMT-0400 (EDT) : true ({"num":"8","success":true,"errors":null})
Connection #9 RETN at Tue May 06 2008 14:24:04 GMT-0400 (EDT) : true ({"num":"9","success":true,"errors":null})
Connection #10 RETN at Tue May 06 2008 14:24:04 GMT-0400 (EDT) : true ({"num":"10","success":true,"errors":null})
Connection #11 RETN at Tue May 06 2008 14:24:09 GMT-0400 (EDT) : true ({"num":"11","success":true,"errors":null})
Connection #12 RETN at Tue May 06 2008 14:24:09 GMT-0400 (EDT) : true ({"num":"12","success":true,"errors":null})
Connection #13 RETN at Tue May 06 2008 14:24:09 GMT-0400 (EDT) : false undefined
Connection #14 RETN at Tue May 06 2008 14:24:09 GMT-0400 (EDT) : false undefined
Connection #15 RETN at Tue May 06 2008 14:24:09 GMT-0400 (EDT) : false undefined
Connection #16 RETN at Tue May 06 2008 14:24:14 GMT-0400 (EDT) : true ({"num":"16","success":true,"errors":null})
Connection #17 RETN at Tue May 06 2008 14:24:14 GMT-0400 (EDT) : true ({"num":"17","success":true,"errors":null})
Connection #18 RETN at Tue May 06 2008 14:24:19 GMT-0400 (EDT) : true ({"num":"18","success":true,"errors":null})
Connection #19 RETN at Tue May 06 2008 14:24:19 GMT-0400 (EDT) : true ({"num":"19","success":true,"errors":null})
Connection #20 RETN at Tue May 06 2008 14:24:24 GMT-0400 (EDT) : true ({"num":"20","success":true,"errors":null})
Connection #21 RETN at Tue May 06 2008 14:24:24 GMT-0400 (EDT) : true ({"num":"21","success":true,"errors":null})
Connection #22 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #23 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #24 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #25 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #26 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #27 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #28 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #29 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined
Connection #30 RETN at Tue May 06 2008 14:24:25 GMT-0400 (EDT) : false undefined


IE:

Connection #1 INIT at Tue May 6 13:24:10 EST 2008
Connection #2 INIT at Tue May 6 13:24:10 EST 2008
Connection #3 INIT at Tue May 6 13:24:10 EST 2008
Connection #4 INIT at Tue May 6 13:24:10 EST 2008
Connection #5 INIT at Tue May 6 13:24:10 EST 2008
Connection #6 INIT at Tue May 6 13:24:10 EST 2008
Connection #7 INIT at Tue May 6 13:24:10 EST 2008
Connection #8 INIT at Tue May 6 13:24:10 EST 2008
Connection #9 INIT at Tue May 6 13:24:10 EST 2008
Connection #10 INIT at Tue May 6 13:24:10 EST 2008
Connection #11 INIT at Tue May 6 13:24:10 EST 2008
Connection #12 INIT at Tue May 6 13:24:10 EST 2008
Connection #13 INIT at Tue May 6 13:24:10 EST 2008
Connection #14 INIT at Tue May 6 13:24:10 EST 2008
Connection #15 INIT at Tue May 6 13:24:10 EST 2008
Connection #1 RETN at Tue May 6 13:24:15 EST 2008 : true ({"num":"1","success":true,"errors":null})
Connection #2 RETN at Tue May 6 13:24:15 EST 2008 : true ({"num":"2","success":true,"errors":null})
Connection #3 RETN at Tue May 6 13:24:20 EST 2008 : true ({"num":"3","success":true,"errors":null})
Connection #4 RETN at Tue May 6 13:24:20 EST 2008 : true ({"num":"4","success":true,"errors":null})
Connection #6 RETN at Tue May 6 13:24:25 EST 2008 : true ({"num":"6","success":true,"errors":null})
Connection #5 RETN at Tue May 6 13:24:25 EST 2008 : true ({"num":"5","success":true,"errors":null})
Connection #16 INIT at Tue May 6 13:24:26 EST 2008
Connection #17 INIT at Tue May 6 13:24:26 EST 2008
Connection #18 INIT at Tue May 6 13:24:26 EST 2008
Connection #19 INIT at Tue May 6 13:24:26 EST 2008
Connection #20 INIT at Tue May 6 13:24:26 EST 2008
Connection #21 INIT at Tue May 6 13:24:26 EST 2008
Connection #22 INIT at Tue May 6 13:24:26 EST 2008
Connection #23 INIT at Tue May 6 13:24:26 EST 2008
Connection #24 INIT at Tue May 6 13:24:26 EST 2008
Connection #25 INIT at Tue May 6 13:24:26 EST 2008
Connection #26 INIT at Tue May 6 13:24:26 EST 2008
Connection #27 INIT at Tue May 6 13:24:26 EST 2008
Connection #28 INIT at Tue May 6 13:24:26 EST 2008
Connection #29 INIT at Tue May 6 13:24:26 EST 2008
Connection #30 INIT at Tue May 6 13:24:26 EST 2008
Connection #8 RETN at Tue May 6 13:24:30 EST 2008 : true ({"num":"8","success":true,"errors":null})
Connection #7 RETN at Tue May 6 13:24:30 EST 2008 : true ({"num":"7","success":true,"errors":null})
Connection #10 RETN at Tue May 6 13:24:35 EST 2008 : true ({"num":"10","success":true,"errors":null})
Connection #9 RETN at Tue May 6 13:24:35 EST 2008 : true ({"num":"9","success":true,"errors":null})
Connection #11 RETN at Tue May 6 13:24:40 EST 2008 : false undefined
Connection #12 RETN at Tue May 6 13:24:40 EST 2008 : false undefined
Connection #13 RETN at Tue May 6 13:24:40 EST 2008 : false undefined
Connection #14 RETN at Tue May 6 13:24:40 EST 2008 : false undefined
Connection #15 RETN at Tue May 6 13:24:40 EST 2008 : false undefined
Connection #16 RETN at Tue May 6 13:24:45 EST 2008 : true ({"num":"16","success":true,"errors":null})
Connection #17 RETN at Tue May 6 13:24:45 EST 2008 : true ({"num":"17","success":true,"errors":null})
Connection #18 RETN at Tue May 6 13:24:50 EST 2008 : true ({"num":"18","success":true,"errors":null})
Connection #19 RETN at Tue May 6 13:24:50 EST 2008 : true ({"num":"19","success":true,"errors":null})
Connection #20 RETN at Tue May 6 13:24:55 EST 2008 : true ({"num":"20","success":true,"errors":null})
Connection #21 RETN at Tue May 6 13:24:55 EST 2008 : true ({"num":"21","success":true,"errors":null})
Connection #22 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #23 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #24 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #25 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #26 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #27 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #28 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #29 RETN at Tue May 6 13:24:56 EST 2008 : false undefined
Connection #30 RETN at Tue May 6 13:24:56 EST 2008 : false undefined

hendricd
6 May 2008, 10:38 AM
Yeah, you need to see how many/which of those are aborts and timeouts.

hendricd
6 May 2008, 10:41 AM
..and probably again with new Ext.data.Connections. Your test involved one shared Connection instance.

malraux
6 May 2008, 11:26 AM
..and probably again with new Ext.data.Connections. Your test involved one shared Connection instance.

I'm not sure I understand the difference...

joku
6 May 2008, 3:30 PM
I don't think newer versions of Firefox or IE follow the RFC in this respect. The two connection rule in the RFC is recognized as an old design.

sigaref
7 May 2008, 2:30 AM
Because multiple queues still rely on maxnumber of connections, this could be solved by introducing some kind of priority system: maybe just a numbering, and higher priorities go directly to the top of the list instead of getting append.

ThorstenSuckow
7 May 2008, 2:32 AM
Because multiple queues still rely on maxnumber of connections, this could be solved by introducing some kind of priority system: maybe just a numbering, and higher priorities go directly to the top of the list instead of getting append.

You might be interested in hendricd's overrides for the ext adapter:

http://extjs.com/forum/showthread.php?t=21681

ThorstenSuckow
16 Apr 2009, 2:38 PM
I have updated the code to work with Ext 3.0RC1 - see the first page.

Unfortunately, Ext.lib.Ajax is now a singleton so you cannot overwrite the methods the way it was done for Ext2.0 - lots of source code has to be copied/pasted,

AFAIK this shouldn't be a big deal since Ext.lib.Ajax is licensed under the BSD (correct me if I'm wrong).

I have also added functionality for determining how many concurrent requests may happen:




/**

* @type {Number} _concurrentRequests The number of max. concurrent requests

* requests allowed.

*/

var _concurrentRequests = 2;



switch (true) {

case Ext.isIE8:

_concurrentRequests = window.maxConnectionsPerServer;

break;



case Ext.isIE:

_concurrentRequests = 2;

break;



case Ext.isSafari:

case Ext.isChrome:

case Ext.isGecko3:

_concurrentRequests = 4;

break;

}



One cool thing about IE8 is that it ships with "window.maxConnectionsPerServer" which holds the number of concurrent requests. It determines the number based on the HTTP protocol used. (6 for 1.1, 2 for 1.0). It does also consider the bandwidth and would allow only 2 concurrent requests for dial-up connections.

JAnderson
29 Sep 2009, 10:30 AM
It's very surprising that there is no official Ext team comment on this issue (and if there is please correct me). As more and more users adopt ExtJS and build complex web-based applications using ExtJS this is going to become a huge problem. As a worst-case scenario people will get upset at ExtJS, assume it is broken and go use some other framework that has better built-in support for handling the browser-based limitations considering concurrent connections. ExtJS should not be claimed to be compatible with IE6 and IE7 with such a gaping hope in existance. Assuming that people are not going to open more than 2 connections with a single server in a complex web app is ludicrous.

JAnderson
29 Sep 2009, 1:01 PM
Here is your code updated for ExtJS 3.0.2. Please make sure I merged your changes properly. I had to update it because there are some seemingly non-trivial changes to the base adapter that are not included in your posted code.



/**
* Overrides ext-adapter to queue AJAX requests.
*/
/*
* Portions of this file are based on pieces of Yahoo User Interface Library
* Copyright (c) 2007, Yahoo! Inc. All rights reserved. YUI licensed under the
* BSD License: http://developer.yahoo.net/yui/license.txt
*/
Ext.lib.Ajax = function()
{
var activeX = [ 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ], CONTENTTYPE = 'Content-Type';

/**
* Added for AJAX queue
* @type {Array} _queue A FIFO queue for processing pending requests
*/
var _queue = [];
/**
* Added for AJAX queue
* @type {Number} _activeRequests The number of requests currently being processed.
*/
var _activeRequests = 0;
/**
* Added for AJAX queue
* @type {Number} _concurrentRequests The number of max. concurrent requests requests allowed.
*/
var _concurrentRequests = 2;

switch (true) {
case Ext.isIE8:
_concurrentRequests = window.maxConnectionsPerServer;
break;
case Ext.isIE:
_concurrentRequests = 2;
break;
case Ext.isSafari:
case Ext.isChrome:
case Ext.isGecko3:
_concurrentRequests = 4;
break;
}

// private
function setHeader(o)
{
var conn = o.conn, prop;
function setTheHeaders(conn, headers)
{
for (prop in headers)
{
if(headers.hasOwnProperty(prop))
{
conn.setRequestHeader(prop, headers[prop]);
}
}
}
if(pub.defaultHeaders)
{
setTheHeaders(conn, pub.defaultHeaders);
}
if(pub.headers)
{
setTheHeaders(conn, pub.headers);
delete pub.headers;
}
}
// private
function createExceptionObject(tId, callbackArg, isAbort, isTimeout)
{
return {
tId : tId,
status : isAbort ? -1 : 0,
statusText : isAbort ? 'transaction aborted' : 'communication failure',
isAbort : isAbort,
isTimeout : isTimeout,
argument : callbackArg
};
}
// private
function initHeader(label, value)
{
(pub.headers = pub.headers || {})[label] = value;
}
// private
function createResponseObject(o, callbackArg)
{
var headerObj = {}, headerStr, conn = o.conn, t, s;
try
{
headerStr = o.conn.getAllResponseHeaders();
Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v)
{
t = v.indexOf(':');
if(t >= 0)
{
s = v.substr(0, t).toLowerCase();
if(v.charAt(t + 1) == ' ')
{
++t;
}
headerObj[s] = v.substr(t + 1);
}
});
}
catch (e)
{
}
return {
tId : o.tId,
status : conn.status,
statusText : conn.statusText,
getResponseHeader : function(header)
{
return headerObj[header.toLowerCase()];
},
getAllResponseHeaders : function()
{
return headerStr
},
responseText : conn.responseText,
responseXML : conn.responseXML,
argument : callbackArg
};
}
/**
* Modified for AJAX queue
* Update _activeRequests and call _processQueue method
*/
// private
function releaseObject(o)
{
o.conn = null;
o = null;

_activeRequests--;
_processQueue();
}
// private
function handleTransactionResponse(o, callback, isAbort, isTimeout)
{
if(!callback)
{
releaseObject(o);
return;
}
var httpStatus, responseObject;
try
{
if(o.conn.status !== undefined && o.conn.status != 0)
{
httpStatus = o.conn.status;
}
else
{
httpStatus = 13030;
}
}
catch (e)
{
httpStatus = 13030;
}
if((httpStatus >= 200 && httpStatus < 300) || (Ext.isIE && httpStatus == 1223))
{
responseObject = createResponseObject(o, callback.argument);
if(callback.success)
{
if(!callback.scope)
{
callback.success(responseObject);
}
else
{
callback.success.apply(callback.scope, [ responseObject ]);
}
}
}
else
{
switch (httpStatus)
{
case 12002:
case 12029:
case 12030:
case 12031:
case 12152:
case 13030:
responseObject = createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false), isTimeout);
if(callback.failure)
{
if(!callback.scope)
{
callback.failure(responseObject);
}
else
{
callback.failure.apply(callback.scope, [ responseObject ]);
}
}
break;
default:
responseObject = createResponseObject(o, callback.argument);
if(callback.failure)
{
if(!callback.scope)
{
callback.failure(responseObject);
}
else
{
callback.failure.apply(callback.scope, [ responseObject ]);
}
}
}
}
releaseObject(o);
responseObject = null;
}
// private
function handleReadyState(o, callback)
{
callback = callback || {};
var conn = o.conn, tId = o.tId, poll = pub.poll, cbTimeout = callback.timeout || null;
if(cbTimeout)
{
pub.timeout[tId] = setTimeout( function()
{
pub.abort(o, callback, true);
}, cbTimeout);
}
poll[tId] = setInterval( function()
{
if(conn && conn.readyState == 4)
{
clearInterval(poll[tId]);
poll[tId] = null;
if(cbTimeout)
{
clearTimeout(pub.timeout[tId]);
pub.timeout[tId] = null;
}
handleTransactionResponse(o, callback);
}
}, pub.pollInterval);
}

/**
* Added for AJAX queue
* Forces AJAX requests to go through the _processQueue method
*/
function asyncRequest(method, uri, callback, postData)
{
var o = getConnectionObject();
if(!o)
{
return null;
}
else
{
_queue.push( {
o : o,
method : method,
uri : uri,
callback : callback,
postData : postData
});
return _processQueue();
}
}

/**
* Added for AJAX queue
* Queues AJAX requests
*/
function _processQueue()
{
var to = _queue[0];
if(to && _activeRequests < _concurrentRequests)
{
to = _queue.shift();
_activeRequests++;
return _asyncRequest(to.method, to.uri, to.callback, to.postData);
}
}
/**
* Renamed for AJAX queue
*/
// private
function _asyncRequest(method, uri, callback, postData)
{
var o = getConnectionObject() || null;
if(o)
{
o.conn.open(method, uri, true);
if(pub.useDefaultXhrHeader)
{
initHeader('X-Requested-With', pub.defaultXhrHeader);
}
if(postData && pub.useDefaultHeader && (!pub.headers || !pub.headers[CONTENTTYPE]))
{
initHeader(CONTENTTYPE, pub.defaultPostHeader);
}
if(pub.defaultHeaders || pub.headers)
{
setHeader(o);
}
handleReadyState(o, callback);
o.conn.send(postData || null);
}
return o;
}
// private
function getConnectionObject()
{
var o;
try
{
if(o = createXhrObject(pub.transactionId))
{
pub.transactionId++;
}
}
catch (e)
{
}
finally
{
return o;
}
}
// private
function createXhrObject(transactionId)
{
var http;
try
{
http = new XMLHttpRequest();
}
catch (e)
{
for ( var i = 0; i < activeX.length; ++i)
{
try
{
http = new ActiveXObject(activeX[i]);
break;
}
catch (e)
{
}
}
}
finally
{
return {
conn : http,
tId : transactionId
};
}
}
var pub = {
request : function(method, uri, cb, data, options)
{
if(options)
{
var me = this, xmlData = options.xmlData, jsonData = options.jsonData, hs;
Ext.applyIf(me, options);
if(xmlData || jsonData)
{
hs = me.headers;
if(!hs || !hs[CONTENTTYPE])
{
initHeader(CONTENTTYPE, xmlData ? 'text/xml' : 'application/json');
}
data = xmlData || (Ext.isObject(jsonData) ? Ext.encode(jsonData) : jsonData);
}
}
return asyncRequest(method || options.method || "POST", uri, cb, data);
},
serializeForm : function(form)
{
var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements, hasSubmit = false, encoder = encodeURIComponent, element, options, name, val, data = '', type;
Ext.each(fElements, function(element)
{
name = element.name;
type = element.type;
if(!element.disabled && name)
{
if(/select-(one|multiple)/i.test(type))
{
Ext.each(element.options, function(opt)
{
if(opt.selected)
{
data += String.format("{0}={1}&", encoder(name), encoder((opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttribute('value') !== null) ? opt.value : opt.text));
}
});
}
else if(!/file|undefined|reset|button/i.test(type))
{
if(!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit))
{
data += encoder(name) + '=' + encoder(element.value) + '&';
hasSubmit = /submit/i.test(type);
}
}
}
});
return data.substr(0, data.length - 1);
},
useDefaultHeader : true,
defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
useDefaultXhrHeader : true,
defaultXhrHeader : 'XMLHttpRequest',
poll : {},
timeout : {},
pollInterval : 50,
transactionId : 0,
/**
* Modified for AJAX queue
* Added else statement to check queue for aborted request
*/
abort : function(o, callback, isTimeout)
{
var me = this, tId = o.tId, isAbort = false;
if(me.isCallInProgress(o))
{
o.conn.abort();
clearInterval(me.poll[tId]);
me.poll[tId] = null;
if(isTimeout)
{
me.timeout[tId] = null;
}
handleTransactionResponse(o, callback, (isAbort = true), isTimeout);
}
else
{
for ( var i = 0, max_i = _queue.length; i < max_i; i++)
{
if(_queue[i].o.tId == o.tId)
{
_queue.splice(i, 1);
break;
}
}
return false;
}
},
isCallInProgress : function(o)
{
// if there is a connection and readyState is not 0 or 4
return o.conn && ! {
0 : true,
4 : true
}[o.conn.readyState];
}
};
return pub;
}();

ThorstenSuckow
29 Sep 2009, 1:40 PM
Here is your code updated for ExtJS 3.0.2. Please make sure I merged your changes properly. I had to update it because there are some seemingly non-trivial changes to the base adapter that are not included in your posted code.

Thanks for your effort. I have updated my local code and it looks good so far. I'm still struggling with updating my code to 3.0.2 and have to admit that I haven't expected that much changes from a patch release. Let's hope things get a little bit easier in the future...

aconran
29 Sep 2009, 6:55 PM
It's very surprising that there is no official Ext team comment on this issue (and if there is please correct me). As more and more users adopt ExtJS and build complex web-based applications using ExtJS this is going to become a huge problem. As a worst-case scenario people will get upset at ExtJS, assume it is broken and go use some other framework that has better built-in support for handling the browser-based limitations considering concurrent connections. ExtJS should not be claimed to be compatible with IE6 and IE7 with such a gaping hope in existance. Assuming that people are not going to open more than 2 connections with a single server in a complex web app is ludicrous.

In general you should avoid generating many simultaneous requests at the same time. One way to combat this is to use batching with a technology like Ext.Direct to batch together your calls and minimize the HTTP pipeline.

We have considered adding a queuing mechanism but at this time it is not on our roadmap. It's a difficult thing to balance, adding functionality, keeping your file size small, preventing unnecessary complexity and making the lib perform well.

aconran
29 Sep 2009, 6:56 PM
I'm still struggling with updating my code to 3.0.2 and have to admit that I haven't expected that much changes from a patch release. Let's hope things get a little bit easier in the future...

Hrm are you talking about the changes that were made to undo the private functions and closures? :-/ These changes were made to make the library easy to override and make our users happy! :D

razvanioan
29 Sep 2009, 11:33 PM
Helo,

Is there a link where I could download the code for ExtJS version 2.2 ?
I'm still working on a project based on 2.2 and I will not migrate it (yet :) )

Thanks,
Razvan

ThorstenSuckow
30 Sep 2009, 12:52 AM
Hi Razvan



Is there a link where I could download the code for ExtJS version 2.2 ?


This should do the trick for 2.2:



/**
* Overwrites ext-adapter behavior for allowing queuing of AJAX requests.
*
*/
Ext.lib.Ajax._queue = [];
Ext.lib.Ajax._activeRequests = 0;
Ext.lib.Ajax._concurrentRequests = Ext.isGecko ? 4 : 2;

Ext.lib.Ajax.abort=function(o, callback, isTimeout)
{
if (this.isCallInProgress(o)) {
o.conn.abort();
window.clearInterval(this.poll[o.tId]);
delete this.poll[o.tId];
if (isTimeout) {
delete this.timeout[o.tId];
}

//aborted may be called with no callback-parameter, so no loadexception
//or else would be generated in handleTransactionResponse.
if (!callback) {
Ext.ux.util.MessageBus.publish('ext.lib.ajax.abort', {
requestObject : o
});
}

this.handleTransactionResponse(o, callback, true);

return true;
}
else {

for (var i = 0, max_i = this._queue.length; i < max_i; i++) {
if (this._queue[i].o.tId == o.tId) {
this._queue.splice(i, 1);
break;
}
}

return false;
}
};

Ext.lib.Ajax.asyncRequest = function(method, uri, callback, postData)
{


var o = this.getConnectionObject();

if (!o) {
return null;
}
else {

this._queue.push({
o : o,
method: method,
uri: uri,
callback: callback,
postData : postData
});

this._processQueue();

return o;
}
};

Ext.lib.Ajax._processQueue = function()
{
var to = this._queue[0];

if (to && this._activeRequests < this._concurrentRequests) {
to = this._queue.shift();
this._activeRequests++;
this._asyncRequest(to.o, to.method, to.uri, to.callback, to.postData);
}


};

Ext.lib.Ajax._asyncRequest = function(o, method, uri, callback, postData)
{
o.conn.open(method, uri, true);

if (this.useDefaultXhrHeader) {
if (!this.defaultHeaders['X-Requested-With']) {
this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
}
}

if(postData && this.useDefaultHeader){
this.initHeader('Content-Type', this.defaultPostHeader);
}

if (this.hasDefaultHeaders || this.hasHeaders) {
this.setHeader(o);
}

this.handleReadyState(o, callback);
o.conn.send(postData || null);

};

Ext.lib.Ajax.releaseObject = function(o)
{
o.conn = null;
o = null;

this._activeRequests--;
this._processQueue();
};

Ext.lib.Ajax.handleTransactionResponse = function(o, callback, isAbort) {

if (!callback) {
this.releaseObject(o);
return;
}

var httpStatus, responseObject;

try {
if (o.conn.status !== undefined && o.conn.status != 0) {
httpStatus = o.conn.status;
} else {
httpStatus = 13030;
}
} catch(e) {
httpStatus = 13030;
}

if (httpStatus >= 200 && httpStatus < 300) {
responseObject = this.createResponseObject(o, callback.argument);
if (callback.success) {
if (!callback.scope) {
callback.success(responseObject);
} else {
callback.success.apply(callback.scope, [responseObject]);
}
}
} else {
switch (httpStatus) {
case 12002:
case 12029:
case 12030:
case 12031:
case 12152:
case 13030:
responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
if (callback.failure) {
if (!callback.scope) {
callback.failure(responseObject);
} else {
callback.failure.apply(callback.scope, [responseObject]);
}
}
break;

default:
responseObject = this.createResponseObject(o, callback.argument);
if (callback.failure) {
if (!callback.scope) {
callback.failure(responseObject);
} else {
callback.failure.apply(callback.scope, [responseObject]);
}
}
break;
}
}

this.releaseObject(o);
responseObject = null;
};

razvanioan
30 Sep 2009, 2:38 AM
I will try it later today.

Thanks,
Razvan

ThorstenSuckow
11 Oct 2009, 10:29 PM
I have updated the code for Ext 3.0.2 on the first page - the last posted override had some major flaws that would prevent the queue from being processed properly. There is also an issue in the current implementation of Ext.lib.Ajax in the abort() method regarding timeouts (see http://www.extjs.com/forum/showthread.php?t=82640) which might not be crucial for the standard Ext.lib.Ajax behavior, but showed up some issues for the queued version.

JAnderson
12 Oct 2009, 8:12 AM
Thanks!

JAnderson
13 Oct 2009, 2:28 PM
Where does this include come from?

Ext.ux.util.MessageBus

JAnderson
13 Oct 2009, 4:00 PM
There is a problem in your posted 3.0.2 code:

This line in createResponseObject:



Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){


should be this:

Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){

The PHP tag breaks the escape characters.

ThorstenSuckow
14 Oct 2009, 1:15 AM
Where does this include come from?

Ext.ux.util.MessageBus

Old fragments of additional code I added to my override ;)

ThorstenSuckow
14 Oct 2009, 1:16 AM
There is a problem in your posted 3.0.2 code:

This line in createResponseObject:



Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){


should be this:

Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){

The PHP tag breaks the escape characters.

Nice catch - I guess I should attach a file to the post so no one suffers from this copy & paste error.

JAnderson
15 Oct 2009, 5:22 PM
Old fragments of additional code I added to my override ;)

So it's safe to just remove that line from your posted 2.2 Ajax queue then?

Also, I really appreciate all of your hard work in writing and maintaining this user extension. I'd hate to point out all sorts of little things and not give you the praise you deserve. This really is a life saver!

ThorstenSuckow
15 Oct 2009, 10:48 PM
So it's safe to just remove that line from your posted 2.2 Ajax queue then?


Sure, it was just there for broadcasting an "abort" message if a request aborts and no callback for that request was defined - thus notifying the application in any case of this failed request.

nitins
18 Mar 2011, 9:49 AM
Hi,

This looks to be very useful plugin. Thanks. Will it work with EXT3.0.1 as well? And, how exactly can I use this plugin in my code. Sorry for being naive, but I am getting a little confused by the PHP code and Code sections provided in the first post. Which code do I have to use exactly to use this plugin in my EXT3.1.0 application?

Thanks,
Nitin

ThorstenSuckow
26 May 2011, 10:59 AM
Just save the code to a file and load it after you have loaded the Ext library! HTH!