4

Imagine in application composed of several different widgets. When an event is fired, the widgets clear out their current data, execute an AJAX request, then put in the new information when the response comes back.

The following is a simplified example of what happens when that event is fired:

for (var i = 0; i < 10; i++) {
    // Do an AJAX post
    $.ajax(document.location.href, {
        data: {
            name: ‘Zach Gardner’
        },
        method: ‘POST’
    });
    // Do some expensive DOM stuff
    for (var j = 0; j < 100; j++) {
        var el = document.createElement(‘div’);
        document.body.appendChild(el);
        for (var k = 0; k < 100; k++) {
            var child = document.createElement(‘div’);
            el.appendChild(child);
            el.removeChild(child);
        }
    }
}

Here is a JSFiddle of the code above

If you open up Fiddler, and run it in Chrome, you'll notice the AJAX requests complete rather quickly.

Chrome
(source: zgardner.us)

But if you do the same thing in IE (tested in 10, 11, 12 preview), you'll notice that the requests take much longer:

IE
(source: zgardner.us)

What we've found is that IE will create the request when jQuery executes a call to the xhr's send method. But it holds onto the request until the call stack has been emptied.

IE Fiddler
(source: zgardner.us)

Notice the significant lag between ClientBeginRequest and ClientDoneRequest. We've found that ClientDoneRequest is always within a few milliseconds of the thread ending.

This only happens for POST AJAX requests. The ClientBeginRequest and ClientDoneRequest for GETs are always within a few milliseconds of each other.

Also, note that this problem also shows up with IE's Dev Tools:

IE Network
(source: zgardner.us)

If you inspect an individual request, you can the second Start, which is when it sends the body of the request, took 4.32 seconds:

IE First Request
(source: zgardner.us)

Why is this happening?

See my blog post for a more detailed explanation.

Community
  • 1
  • 1
Zach Gardner
  • 999
  • 2
  • 10
  • 13
  • Which IE version were you testing? – Powerlord Aug 13 '14 at 18:52
  • I would be more cautious. Your blog posts says that companies should know about this issue, while I don't think companies would be programming their Web apps based on the concept of your sample code! – Matías Fidemraizer Aug 13 '14 at 18:54
  • @Powerlord I've tested this in IE 10, 11, and the 12 developer preview. – Zach Gardner Aug 14 '14 at 14:50
  • The links in your post don't seem to work anymore. http://zgardner.us doesn't resolve to anything. Which is a pity, because I think I'm experiencing the same issue that you describe but I can't verify it. Please consider updating the links with some public image hosting services. Thanks – AsGoodAsItGets Jun 21 '17 at 09:21

2 Answers2

4

Since Internet Explorer's source code isn't freely available, only an IE developer should be able to answer your question with more degree of detail.

Web browsers implement asynchronousity using an execution queue. There's a single thread and it attends enqueued tasks from the UI and AJAX (and other task sources). That is, it can only happen an operation at once.

In the other hand, UI is prioritized over any other task, thus, DOM operations should be executed before an AJAX call. When you send an AJAX request, maybe there's a small CPU time to send the request, but if you do a lot of UI work, it might take longer to end the whole AJAX request because UI > AJAX in terms of prioritization.

The case you're describing in your question is an implementation detail in Internet Explorer. Chrome and Firefox might work better in terms of task prioritization, but anyway, I don't imagine a case where you try to append 100 DOM elements at once while you send a bunch of AJAX requests. It's you who needs to optimize your code rather than expecting your Web browser to optimize an academic/edge case.

For example, you might not add 100 DOM elements one by one to the document element. You might create a container (i.e. document.createElement("div")), and later add 100 elements while container isn't attached to the DOM to finally add the whole entire container to the DOM. This will add these 100 elements in a single paint event, which is cheaper and other stuff like AJAX should be executed in less time because of task prioritization.

Also, I'm not sure if you know that Fiddler slow-downs requests (it's a debugger, there's a performance hit).

You should take a look at this other Q&A here in SO: How does JavaScript handle AJAX responses in the background?

Community
  • 1
  • 1
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • "It's you who needs to optimize your code rather than expecting your Web browser to optimize an academic/edge case." If I execute an asynchronous operation in a normal language (e.g. create a new thread), the operation is executed independent of my current thread. It is a fundamental assumption that AJAX requests are asynchronous, and should be sent to the server in an asynchronous manner. Further, why would IE immediately send a GET but wait till the end for a POST? "Also, I'm not sure if you know that Fiddler slow-downs requests" We've used TCPview, and see the same behavior. – Zach Gardner Aug 13 '14 at 18:56
  • @ZachGardner You can argue whatever, but Web browsers, either IE, Chrome or Firefox, are single-threaded!!! Asychronousity in Web development approach is a single thread dequeuing work. – Matías Fidemraizer Aug 13 '14 at 18:58
  • @ZachGardner Just think how easily you're able to access the DOM from an AJAX callback without synchronizing your code with the UI thread. If Web browsers would execute AJAX using multi-threaded approach, you woudln't be able to access the DOM from an AJAX callback. Furthermore, Web Workers are multi-threaded and you CAN'T access the UI from a Web Worker, but you can send AJAX requests!! – Matías Fidemraizer Aug 13 '14 at 19:01
  • I know, I'm just saying that the request is expected to be processed asynchronously. The response may have to be processed in the next thread. If the client sends the request then does some work, both the client and the server can be working at the same time. – Zach Gardner Aug 13 '14 at 19:54
  • @ZachGardner And this is the expected behavior until the Web browser receives an HTTP response. How IE handles it is an implementation detail. I guess you're not going to go too far here with your question :( – Matías Fidemraizer Aug 13 '14 at 20:53
  • @ZachGardner BTW I doubt that IE can't handle UI stuff while an AJAX response is awaited... Do your measurements without Fiddler. Have you checked this behavior with IE developer tools, network tab...? – Matías Fidemraizer Aug 13 '14 at 20:54
  • I've added screenshots of the IE Dev Tools network tab that I captured while running the JSFiddle I posted. It shows the same thing as Fiddler. – Zach Gardner Aug 14 '14 at 11:37
0

Both browsers make Ajax calls when the browser is "idle." Chrome is so much better at manipulating the DOM than IE, that it only seems like it makes the Ajax calls immediately.

However, if you remove this line:

el.removeChild(child);

... you'll notice that Chrome takes approx. four times longer to make the first Ajax call, followed by nine calls in rapid succession.

Chrome may do some optimization when it's asked to create then remove the same node. This may be considered an "edge case," which Chrome happens to be better at than IE.

In any case, code optimization is our best way of handling this situation until multi-threaded JavaScript comes along.

Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
  • Here is an example where I do no UI updates, but still spend some time calculating a factorial in three different ways: http://jsfiddle.net/zgardner/bfjw1k79/5/ Chrome immediately sends the requests off, but IE holds on to them until all my calculation is done. Note that my expensive calls are in a different thread, so IE should be "idle" immediately after the outer for loop ends. – Zach Gardner Aug 18 '14 at 12:39
  • When I view the factorials program on Chrome's Network tab, I see "(pending)" until the factorials are calculated, and I see "Finished" afterwards. I don't know if "(pending)" means that Chrome has sent the request and is waiting for a response, or if it means that Chrome has simply added the request to the execution queue. A WireShark analysis may answer this. – Rick Hitchcock Aug 18 '14 at 15:28
  • Here is what a Fiddler trace looks like for Chrome http://zgardner.us/wp-content/uploads/2014/08/chrome1-1024x545.png and IE http://zgardner.us/wp-content/uploads/2014/08/ie2-1024x545.png Notice how IE has the big blue blocks. That is the time it spends holding onto the request. If you read my initial blog on this post, you'll see how to reproduce it: http://zgardner.us/2014/08/06/ie-ajax-post-requests/ – Zach Gardner Aug 18 '14 at 18:25
  • Thanks, I wasn't familiar with the Fiddler Debugger, but it does seem to confirm your suspicions. – Rick Hitchcock Aug 18 '14 at 19:10