Why does IE wait till the call stack is empty to s

2019-03-31 02:40发布

问题:

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 http://zgardner.us/wp-content/uploads/2014/08/chrome.png

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 http://zgardner.us/wp-content/uploads/2014/08/ie.png

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 http://zgardner.us/wp-content/uploads/2014/08/ie1.png

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 http://zgardner.us/wp-content/uploads/2014/08/ie-dev-tools.png

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 http://zgardner.us/wp-content/uploads/2014/08/ie-timings.png

Why is this happening?

See my blog post for a more detailed explanation.

回答1:

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?



回答2:

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.