In most ways, the recent revelations about IE11 are amazing and awesome and I <3 <3 them. However, one particular side-effect of this announcement is very very unawesome. It's clearly a case of throwing the baby out with the bathwater.
IE11, PLEASE PLEASE re-consider your decision (as described in this blog post) and bring back “real script preloading”. PLEASE!
As @slicknet points out in “Internet Explorer 11: Don’t call me IE”, IE11 has eschewed many (all?) of their old non-standard APIs so that all the old “isIE” type “feature tests” (not really, but whatever) will fail, and IE11 should be served “standards-based” content.
This is a super understandable and admirable move on their part.
So what’s the problem? One of these changes forces IE11 to take a big step backward in terms of performance/performance-flexibility with regards to script loading.
Put simply, if you want to dynamically load a bunch of scripts in parallel, but control their execution order (to ensure dependencies), the web platform doesn’t really give you very many options.
One option you have, which I call “ordered async”, was something I helped get added to the spec a couple of years ago. It amounts to setting
script.async = false on dynamically created script elements. This tells the modern, spec-conforming browsers to load any/all such scripts in a parallel script loading queue, but to make sure their executions are in request-order, regardless of load completion.
In a lot of cases, this is sufficient. If you have just one grouping of scripts to load in parallel, and you want all of them to execute strictly in request-order, great. If, however, you need to load two or more separate groups of scripts (say “A-B-C” and “D-E-F”), where there’s no relationship between the two groups, and you want those groups to operate independently, you’re just SOL.
Or, if you want in your one grouping of scripts to have “A” and “B” run (in whatever-order), then “C”, then “D” and “E (in whatever order), you’re also out of luck.
Both of those use-cases will function fine with “ordered async”, but with less-than-optimal performance. There’s just one “ordered async” queue in the browser, and all your scripts that you do
.async = false on will go into that queue, and will load in parallel but will strictly execute in request-order, meaning some executions may be delayed unnecessarily because you cannot express those more complex execution profiles with the web platform as it stands.
I’m glad we have “ordered async”, but I’m sad that we only have “ordered async”, because I quite often run across real-world scenarios where I need more control, and I don’t have it, so I just accept lower performance.
IE to the rescue!
Since IE4, however, there’s been a silver bullet for this problem. It’s the ultimate in performance-flexibility. I call it “real script preloading”. It’s superior (in terms of performance and performance-flexibility) to all other browsers, even today.
What this technique boils down to is being able to load many scripts in parallel, but allowing your code to be fully in control of what executes when, and in what order. You don’t have to rely on the browser’s “ordered async” queue semantics — your code has complete control. Performance++ and Flexibility++.
How it works is that IE (from version 4 to 10) will start loading (aka “preloading”) a script in the background, as soon as you set a
src attribute on a dynamically created script element. However, that script may complete loading but will not execute until you actually insert that script element into the DOM.
So, you can “preload” any number of scripts, and then choose what order you want them to execute in by simply inserting them into the DOM as you see fit.
It’s amazing in its simplicity and power.
Before you assume this is just some proprietary non-standard thing IE does, consider this wording in the spec:
For performance reasons, user agents may start fetching the script as soon as the attribute is set, instead, in the hope that the element will be inserted into the document. Either way, once the element is inserted into the document, the load must have started. If the UA performs such prefetching, but the element is never inserted in the document, or the src attribute is dynamically changed, then the user agent will not execute the script, and the fetching process will have been effectively wasted.
But, this technique, to be effective, actually relies on something that is non-standard (as of yet), which is that to take advantage of on-demand-execution, you’ll need to be able to detect that the script has finished “preloading”.
onload event doesn’t do that. Quixotically,
script.onload doesn’t just mean “loaded”, it means “loaded and executed”. Chicken-and-the-egg here.
onload won’t fire until the execution is complete, so you can’t detect that the load (preload) finished with that event. There’s no
preload event, or
loaded, or whatever, that tells us only the load was finished.
BUT, IE (from version 4 to 10) gave us a non-standard extension which really was the crux of the “real script preloading” technique. They have supported a
script.readyState property which indicates the loading progression, including “loaded” for loading being finished (regardless of execution).
So, you can preload all your scripts, and listen for the
readystatechange event to notify you that the
script.readyState property has changed, and if it goes to “loaded”, you know the preload is complete, and that script can now be executed, whenever you want it to be.
It’s not complicated. It’s actually quite graceful. And super performant and flexible.
More than 2 years ago, I wrote this behavior into LABjs script loader. And it’s been working beautifully and performantly in IE all this time.
Fly in the ointment
IE11 has announced they are removing
script.onreadystatechange. This sucks. Because it completely disables and neuters the “real script preloading” technique.
I don’t know know whether IE11 is removing the actual preloading or not, but if you can’t observe the preload completing, then it’s moot, as far as this script loading technique is concerned.
Luckily, in LABjs, I also detect and use “ordered async” if present in the browser.
So, the “good news” is, regardless of IE11’s change, LABjs will continue to operate fine, because IE10 added “ordered async”. Phew.
But it really sucks that IE11 will be taking a step backwards (compared to IE4 – IE10!) in terms of this performance and performance-flexibility.
Script Execution Control
My proposal was to standardize what IE4+ was already doing. My primary argument was “IE4+ is already doing it, so that’s less work for other browsers to come into line with it.” Of course, in light of this revelation, my argument basically falls apart.
Nicholas’ proposal was slightly different, but the same spirit. I could fully support it, too. In fact, LABjs already has a future-thinking feature detect for it!
Either way, the WHATWG needs to act on one of these proposals, or something else, and give us “real script preloading”. I’ve been petitioning for that for 2 years, and have been stonewalled by Ian Hickson.
Regardless, IE11, please re-consider your decision specifically with relation to
Please don’t take a step backward in performance and performance-flexibility compared to IE10. Please don’t abdicate your leader-status on script loading capability and performance compared to all other browsers.
If you have to, please help me lead the efforts to finally convince WHATWG to give us something standard, and implement whatever that is, into IE11. Even if it’s not what has already been proposed (or what you already do in IE4 – IE10), whatever it is, if it’s practical, I’ll put it into LABjs so people can take advantage of it.
Please Microsoft. Please IE11. Save “real script preloading”!