Script Execution Control
This page is intended to formalize the discussion around the ability to control (aka, defer until desired) the execution of a script, to occur sometime after the script finishes loading.
This feature, with two main proposals for how to accomplish it, has been discussed on in a long email thread on the WHATWG list: "Proposal for separating script downloads and execution"
This page is being created to document that first proposal, as well as a second proposal (which actually came as a result of feedback on another WHATWG wiki page/proposal: Dynamic_Script_Execution_Order) for the same functionality. These are the two main solutions being discussed at the moment. This page will also attempt to distill some of the feedback and other (unfortunately unsuitable) proposals discussed thus far.
Please feel free to join the discussion of this topic in the Talk:Script_Execution_Order_Control area.
Use Case Description
The use-case is that one or more scripts may be necessary to download, but not to automatically execute. The reasons for wanting to defer the execution of a script element until on-demand later are varied. They include performance issues with limited CPU's on mobile devices, the ability to choose based on user-action which of two scripts should be applied, the ability to delay the execution of a script until the user-action activates some part of a page, etc.
In addition, this functionality as described will be useful for parallel script loaders, giving them the ability to download multiple scripts in parallel, but control their execution order by deferring the execution of all of them, and then once they are all loaded, executing them on-demand in desired order. The base-version of this application of the use-case was addressed by the "async=false" behavior, recently adopted by the Specification, but would be bolstered and improved by the functionality in these current proposals.
There's no way to defer the execution of a script element, except by use of the `defer` attribute in markup script elements (meaning it's inaccessible to dynamic script loaders). However, the `defer` attribute in markup still only allows deferring until after DOMContentLoaded (aka, "DOM-ready"). Several applications of the use-case call for an abitrary, author-controlled point to decide when scripts should execute, not locked to DOM-ready.
With regards to the "async=false" behavior being improved with this proposal, script loaders cannot currently (with "async=false") specify multiple "groups" of scripts which have order-dependency within the group, but the groups themselves are completely independent. There's no way to currently download all scripts in groups 1 and 2, but allow either group 1 or group 2 to execute when the first one finishes. Currently, the only way to do this based on specification is to use "async=false", and then all the scripts will execute in the order added to the DOM -- the "independent grouping" of execution is not possible.
Current Usage and Workarounds
Currently, this functionality is available in IE (since version 4). To achieve this functionality in other browsers, there's two main types of hacks to accomplish it:
1. The content may be "wrapped", for instance in /* ... */ comments, such that its automatic execution will be moot. Then, the page can, at the desired time, grab the script content, remove the comment markers, and re-execute the code. This technique only works for inline script elements, not for externally loaded script elements (which also means that any caching benefit is lost).
2. The page can employ one of a variety of "cache preload" tricks (different for each browser), whereby the script is loaded into the cache but not executed, and then when execution is desired, the script is re-requested with a proper script element, and thus executed. The main drawback to this approach is the assumption of cacheability. If a script was not properly cached, this technique will result in a costly double-load. Also, as stated, these hacks are brittle and require different variations for each browser. They are quite susceptible to future restrictions by the Specification, or by changes to browsers' optimization techniques.
The primary benefit underlying all the use-cases of this proposal is performance. It gives the page more control over execution of downloaded scripts, allowing the page author to make tradeoffs on when scripts are executed in an attempt to optimize for the user-conditions and experience.
The secondary benefit is to reduce (greatly) the complexity (and hackiness) of script loaders (like LABjs, ControlJS, etc) which go to great lengths to employ tricks and hacks to accomplish this described functionality in existing browsers.
External Discussions of this Feature
Proposal 1 (Nicholas Zakas)
This proposal is described in detail in Nicholas Zakas' Delayed Script Execution v2.1, but will be summarized here for clarity of this page.
This proposal recognizes the need for an event mechanism to detect when a script is preloaded, as well as the need for a feature-detect to detect if the functionality is implemented in a browser.
The proposal is:
1. Add a `preload` property to dynamic script-created script elements (*not* markup script elements), which defaults to false, that can be set to `true` *before* the setting of the `src` property. Turning on "preload" will cause the script to be loaded immediately, even if the script has not yet been added to the DOM.
2. Add a `onpreload` event, which fires when the script element finishes preloading. The script element can then, at that event time, or later, be executed by adding the script element to the DOM through normal procedures.
3. The feature-detect will be:
(typeof document.createElement(“script”).preload == “boolean”)
The goal of this proposal is to be as semantic as possible to what is actually occurring (thus the use of the very descriptive and appropriately named `preload` and `onpreload`).
1. This proposal's main limitation is that it requires a new property and a new event (which is not precedented anywhere else in the HTML specification yet). That means that the proposal will require more effort on the part of the Specification as well as on the part of browsers to implement.
2. In fact, for full adherence to this proposal, it will actually require a change to IE's existing behavior, to make it not "preload" if the `preload` attribute is not set. This is quite possible to create backwards-compat concerns from the IE team.
Proposal 2 (Kyle Simpson)
This proposal is, in spirit, very similar to Proposal 1, but is a little bit simpler (in that it doesn't add a new property or unprecedented event mechanism). Whereas Proposal 1 seeks to add a `preload` attribute to the script element to signal that it should have the preloading (load-but-not-execute) behavior, this proposal builds on automatic behavior for preloading already suggested by the current HTML Specification:
Specifically, in step 12 (which is about fetching a `src` URL):
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.
Put simply, this wording says that a script may begin downloading as soon as the `src` property is set on a dynamic script element, even if that element is not added to the DOM. Further, it suggests that adding the element to the DOM is necessary to execute the script. From this, we can infer that a script may be "preloaded" by creating and keeping references to script elements, but not adding them to the DOM. And then when the script is desired to execute, the script element can be added to the DOM.
In addition to this wording already being in the specification, the functionality as described has been implemented in IE since version 4.
As with the proposal from Nicholas Zakas, this proposal also requires an event mechanism which can allow the web author to be notified of when a script element is finished preloading. In addition, both proposals require a "feature-detect" so that web authors can opt-in to the described behavior after detecting that it is present in a browser.
The proposal is:
1. Change the above referenced wording (as necessary) to indicate that the preloading behavior is required rather than being a performance suggestion.
2. Taking IE's implementation as the pattern, specify the `readyState` property on the script element (defaults to "uninitializd"), and fire `onreadystatechange` events when the script is "loaded" (finished preloading) and "complete" (finished executing, ~ similar to `onload`). The `readyState` functionality is already precedented in the specification for the XHR object, so that same pattern and wording could/should be applied here.
3. The feature-detect will be:
(document.createElement("script").readyState == "uninitialized")
The reason for checking not only the `readyState` variable but also its value is that Opera currently implements a non-functional `readyState` property on the script element (but does NOT support the proposed functionality), but Opera's default value is "complete". The feature-test would give a false-positive in Opera if the value isn't included in the test. Opera has stated in the WHATWG discussion thread that they would not not change the default value of `readyState` to "uninitialized" unless they were also implementing the behavior described in this proposal. No other known browsers have `readyState` on script elements, and the assumption is that any new browser implementations adopting it would adhere to the specification (if this proposal is adopted). So, the proposed feature-detect should be reliable.
This proposal allows scripts to be added to the DOM before they are finished loading, as they currently can, with no change in current behavior. It also does not specify any different model for script execution than is currently present in browsers. In other words, under the circumstances that a browser would execute a script synchronously, the same would be true, and under the circumstances a script would be queued for execution asynchronously as soon as possible, the same would also be true.
The goal of this proposal is to make the minimum change to both specification and browser behavior necessary to fulfill all the goals of the stated use-case. It does not introduce any new, unprecedented elements or concepts, and instead builds on existing specification wording and actual browser implementation.
1. The feature-detect is riskier/weaker/more brittle, and not as graceful as in Proposal 1
- Yes, it's less graceful of a feature-detect. But as explained, it *is* functional. Given appropriate evangelism and involvement with browser vendors, if this proposal were implemented as described, the feature-detect should not be risky.
2. The proposal does not address the use-case of deferring inline script elements' execution
- Yes, neither of the proposals are seeking to fill this use-case, but this proposal is not really capable of it, whereas the other proposal *could* be extended to include markup script element support for that use-case. There's not yet been sufficient evidence of that use-case being necessary.
Insufficient Solution Suggestions
TODO: fill out this section with the other suggestions that have been discussed but deemed insufficient for the use-case.