views:

2209

answers:

3

Hello,

I'm not sure where the problem is...

I have an ajax request that checks the tracking information for a package on a page with a list of packages:

$(".fedex_status").each(function() {

    var item = this;

    // some code to construct tracking_url

    $.ajax({
         type: "GET", 
         url: tracking_url, 
         async: true, 
         cache: false, 
         success: function(data) { $(item).html("(" + data  + ")"); }, 
         error: function() { $(item).html("request failed...");} 
         });
 });

So if there are 10 packages on the page (10 things with class 'fedex_status') 10 requests are created. The requests work fine, but results are returned one at a time (in a serial manner). I added a timestamp to the start and stop of a request within the controller action:

    public ActionResult Fedex(string trackingNumber)
    {
        DateTime requestStart = DateTime.Now;

        TrackingService tracking = new TrackingService();

        string status = tracking.FedexTrackingNumberStatus(trackingNumber);

        return Content(status + " - " + requestStart.ToString("hh:mm:ss.FFF") + " - " + DateTime.Now.ToString("hh:mm:ss.FFF"));
    }

None of the timestamps overlap. So the controller is processing the requests one at a time. This seems crappy.

Now, the ajax request 'should' be parallel. It definitely returns immediately (its asynchronous). When I look at the IIS logs the requests have the same timestamp as the controller action return.

So my question is: is jquery not sending all the ajax requests in parallel or is IIS or ASP.NET only processing requests serially. I'm a big noob with IIS and the technical details of ASP.NET, so maybe its been misconfigured forever and only responds to one request at a time on everything (its internal use only, low traffic). That said, I'm also an idiot at jquery and not sure how to test when the requests are actually being fired (all I know is the $.ajax call returns immediately).

Thanks!

A: 

You are calling your ajax request every time you find an element with the class .fedex_status.

Your line

$(".fedex_status").each

is saying do this everytime you find an element with the class .fedex_status.

Rigobert Song
Yup, thats the idea. So if I have 10 matches for fedex_status I want to call 10 ajax requests in parallel and have each return asynchronously. Right now they are being done serially (not really synchronously though... the requests returns immediately, so its asynchronous, but the requests are handled serially). I don't know if its client side or server side that is making things be serial instead of parallel.
eyston
The request will not run in parallel because your calling them one after the other!I dont know much about JSON but you could return the info in JSON in one ajax request then loop through the json and insert the data into the dom. Not sure just an idea.
Rigobert Song
So there can only be 1 ajax request in flight at a time?
eyston
There's `async: true` flag, so the requests a being sent in parallel, but are being processed serially on the server side. BTW, I would recommend using something like Fiddler to debug your HTTP session.
DreamSonic
@Rigobert is mistaken here. There can be multiple AJAX requests at a time! The callback specified in the AJAX object is invoked when the request returns, so the .Each() function does not block until the request completes; it continues immediately after the request is dispatched and that request runs on a background thread in the browser.
Rex M
@DreamSonic: Thanks, I'll try Fiddler.
eyston
+1  A: 

In theory, ASP.NET with Session state enabled locks the session for each user request, thus processing them serially. I don't think you can disable Session for an MVC project painlessly, because TempData for example uses session by default. But you can try.

EDIT: here's a related question

DreamSonic
This is unfortunate. I actually use temp data in other places (messages to the user like 'shipment created!' and crap like that) so I can't just get rid of it. This specific controller will never need TempData or Session though. I wonder if disabling TempData on the controller level will help any, or the fact that session isn't disabled in the web.config trumps whether the controller uses it or not.
eyston
I thought that was only true in a subset of circumstances (aspcompt=true comes to mind). I don't think session, by itself, causes all requests to be serialized.
Erv Walter
@Erv: The bit of reading I've done about it since this answer is that in standard ASP.NET you can specify what type of lock you want on session on a per page basis. By default I think its a writer lock and that only allows 1 request per page. You can change it to a reader lock which will allow multiple readers, but will block writers, and will block on a writer holding the lock. I have no idea how to make any of this apply to ASP.NET MVC. I'd love to just say 'use a reader lock everywhere' because I use session state for nothing special.
eyston
I created a WebForm Page with the directive 'EnableSessionState="False"' that performed the same function as the ASP.NET MVC Action. I'm definitely getting 2 parallel requests at once in IE7 (which is the per domain limit yah?). I tried it in chrome and I think its a bit more than 2 but hard to tell exactly. It seems in my case that it is session related at least. Next question is how to work around this in ASP.NET MVC. Thanks all!
eyston
+1  A: 

Your code works as expected on my machine. I had to insert any artificial delay with Thread.Sleep to see it in action though because the unadulterated requests executed so fast that it appeared they were all completed at once.

 System.Threading.Thread.Sleep(1000);

 return Content(String.Format("Thread #{0}, Started = {1:hh:mm:ss.FFF}, Completed = {2:hh:mm:ss.FFF}, Duration = {3:hh:mm:ss.FFF}, Result = {4}",
       System.Threading.Thread.CurrentThread.ManagedThreadId,
       requestStart,
       DateTime.Now,
       DateTime.Now.Subtract(requestStart),
       status));

A quick test on my machine using Firefox and connecting to the built-in Visual Studio Web Server showed that two requests were executed simultaneously on the client and handled by two different threads on the server.

As far as I know, there are a limited number of allowed concurrent requests allowed by the browser. Here is another post that on SO that covers what each value is for each of the popular browsers: How many concurrent AJAX (XmlHttpRequest) requests are allowed in popular browsers?

EDIT: I reread your post and saw that you mentioned IIS 6. I do not have access to IIS 6, but running it on an IIS 7 Web Server with a 5 second artificial timeout (Thread.Sleep) showed that multiple requests were being handled at the same time by different server threads. None of them start at the EXACT same time, so there might be some queuing going on at the server, but with a 5 second delay it's easy to see that in Firefox 3.0 I have at least 6 concurrent requests at a time.

GuyIncognito
I'll have to play around with this some more. The action code in my ASP.NET MVC implementation is identical to the ASP.NET WebForms code (other than WebForms parsing the query-string). Other than that the only difference was the change in disabling session in WebForms. I'm positive I'm getting better performance out of WebForms than ASP.NET MVC due to some kind of locking. If you are getting 2 simultaneous requests out of ASP.NET MVC then I need to dig deeper. Thanks!
eyston
Is there are reason that you wouldn't want to do a single request with all of the tracking numbers and then parse the data returned and update the UI instead of multiple calls?
GuyIncognito
A few, most notably that this is easier. But even ignoring that, if I sent a request with an array of tracking numbers I then have to do them serially at the server level or be responsible for spawning a bunch of threads or make my service asynchronous. Why go through that hassle when I have a http and a web server to do it all for me. In a general case (not for tracking info) I would like to explore something like igoogle widgets that do all their loading over ajax. So you have a portal home and then a bunch of ajax calls to load the page.
eyston
@ your edit: Obviously 6 concurrent requests is parallel enough for me. It was just with a single request it could take 5-10 seconds. Even with just two at once it was a huge improvement (it smokes in FF3 / Chrome). I'm happy with the performance now via WebForms but do want to look closer at getting ASP.NET MVC to the same level. The difference between IIS6 and 7 is interesting. I have Vista at home but only 2k3 / XP at work. I can give it a shot at home I think.
eyston