I have been sorting through this issue all day and hope someone can help pinpoint my problem. I have created a "asynchronous progress callback" type functionality in my app using ajax. When I strip the functionality out into a test application I get the desired results. See image below:
Desired Functionality
When I tie the functionality into my single page application using the same code I get a sort of blocking issue where all requests are responded to only after the last task has completed. In the test app above all request are responded to in order. The server reports a ("pending") state for all requests until the controller method has completed. Can anyone give me a hint as to what could cause the change in behavior?
Not Desired
Desired Fiddler Request/Response
GET http://localhost:12028/task/status?_=1383333945335 HTTP/1.1
X-ProgressBar-TaskId: 892183768
Accept: */*
X-Requested-With: XMLHttpRequest
Referer: http://localhost:12028/
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
Connection: Keep-Alive
DNT: 1
Host: localhost:12028
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 3.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcVEVNUFxQcm9ncmVzc0Jhclx0YXNrXHN0YXR1cw==?=
X-Powered-By: ASP.NET
Date: Fri, 01 Nov 2013 21:39:08 GMT
Content-Length: 25
Iteration completed...
Not Desired Fiddler Request/Response
GET http://localhost:60171/_Test/status?_=1383341766884 HTTP/1.1
X-ProgressBar-TaskId: 838217998
Accept: */*
X-Requested-With: XMLHttpRequest
Referer: http://localhost:60171/Report/Index
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
Connection: Keep-Alive
DNT: 1
Host: localhost:60171
Pragma: no-cache
Cookie: ASP.NET_SessionId=rjli2jb0wyjrgxjqjsicdhdi; AspxAutoDetectCookieSupport=1; TTREPORTS_1_0=CC2A501EF499F9F...; __RequestVerificationToken=6klOoK6lSXR51zCVaDNhuaF6Blual0l8_JH1QTW9W6L-3LroNbyi6WvN6qiqv-PjqpCy7oEmNnAd9s0UONASmBQhUu8aechFYq7EXKzu7WSybObivq46djrE1lvkm6hNXgeLNLYmV0ORmGJeLWDyvA2
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcSUxlYXJuLlJlcG9ydHMuV2ViXHRydW5rXElMZWFybi5SZXBvcnRzLldlYlxfVGVzdFxzdGF0dXM=?=
X-Powered-By: ASP.NET
Date: Fri, 01 Nov 2013 21:37:48 GMT
Content-Length: 25
Iteration completed...
The only difference in the two requests headers besides the auth tokens is "Pragma: no-cache" in the request and the asp.net version in the response.
Thanks
Update - Code posted (I probably need to indicate this code originated from an article by Dino Esposito )
var ilProgressWorker = function () {
var that = {};
that._xhr = null;
that._taskId = 0;
that._timerId = 0;
that._progressUrl = "";
that._abortUrl = "";
that._interval = 500;
that._userDefinedProgressCallback = null;
that._taskCompletedCallback = null;
that._taskAbortedCallback = null;
that.createTaskId = function () {
var _minNumber = 100,
_maxNumber = 1000000000;
return _minNumber + Math.floor(Math.random() * _maxNumber);
};
// Set progress callback
that.callback = function (userCallback, completedCallback, abortedCallback) {
that._userDefinedProgressCallback = userCallback;
that._taskCompletedCallback = completedCallback;
that._taskAbortedCallback = abortedCallback;
return this;
};
// Set frequency of refresh
that.setInterval = function (interval) {
that._interval = interval;
return this;
};
// Abort the operation
that.abort = function () {
// if (_xhr !== null)
// _xhr.abort();
if (that._abortUrl != null && that._abortUrl != "") {
$.ajax({
url: that._abortUrl,
cache: false,
headers: { 'X-ProgressBar-TaskId': that._taskId }
});
}
};
// INTERNAL FUNCTION
that._internalProgressCallback = function () {
that._timerId = window.setTimeout(that._internalProgressCallback, that._interval);
$.ajax({
url: that._progressUrl,
cache: false,
headers: { 'X-ProgressBar-TaskId': that._taskId },
success: function (status) {
if (that._userDefinedProgressCallback != null)
that._userDefinedProgressCallback(status);
},
complete: function (data) {
var i=0;
},
});
};
// Invoke the URL and monitor its progress
that.start = function (url, progressUrl, abortUrl) {
that._taskId = that.createTaskId();
that._progressUrl = progressUrl;
that._abortUrl = abortUrl;
// Place the Ajax call
_xhr = $.ajax({
url: url,
cache: false,
headers: { 'X-ProgressBar-TaskId': that._taskId },
complete: function () {
if (_xhr.status != 0) return;
if (that._taskAbortedCallback != null)
that._taskAbortedCallback();
that.end();
},
success: function (data) {
if (that._taskCompletedCallback != null)
that._taskCompletedCallback(data);
that.end();
}
});
// Start the progress callback (if any)
if (that._userDefinedProgressCallback == null || that._progressUrl === "")
return this;
that._timerId = window.setTimeout(that._internalProgressCallback, that._interval);
};
// Finalize the task
that.end = function () {
that._taskId = 0;
window.clearTimeout(that._timerId);
}
return that;
};
Update 1 - Many thanks to John Saunders. I was able to locate this article that explains what John was implying in his comment about serialized access
"It blocks parallel execution and forces parallel requests to be executed one after another because the access to ASP.NET Session state is exclusive per session"