A user account is required in order to edit this wiki, but we've had to disable public user registrations due to spam.

To request an account, ask an autoconfirmed user on Chat (such as one of these permanent autoconfirmed members).

Dynamic Script Execution Order

From WHATWG Wiki
Jump to navigation Jump to search

This page is intended to formalize the discussion around an important but currently underserved use-case (in both spec and various browser implementations): the need for a dynamic script loading facility that can download resources in parallel but ensure that they execute serially in insertion order, for dependency reasons.

*Update: The main proposal listed here, also known as "async=false", was recently officially adopted by Ian Hixie into the HTML Specification. Getify 00:31, 14 February 2011 (UTC)

A long email thread on the W3C public-html list, which began here and for which a recent message is here, has been discussing this problem, but the email thread is becoming unruly and hard to track, so this wiki page will now be the official location to discuss the topic.

Briefly, this issue has arisen because recent "nightlies" changes in both Mozilla Gecko and Webkit have broken the ability for script-loaders like LABjs to be able to download scripts in parallel but ensure their execution order. As a result of the discussion, it's become apparent that both the spec for, and various current browser implementations around, dynamic script loading is incomplete in addressing this use case, and that some change needs to occur.

There is one main proposal that has surfaced from the discussions, with several other alternatives having been discussed. This page will try to distill the long email thread down and clearly present the main proposal, objections, and alternative suggestions.

Please feel free to join the discussion of this topic in the Talk:Dynamic_Script_Execution_Order area.

Use Case Description

Script tags/elements can either:

a) appear in the HTML markup ("parser-inserted"), OR

b) be dynamically appended to the DOM using document.createElement() ("script-inserted")

Parser-inserted script tags, up until recent browser versions, had the undesirable performance behavior of loading and executing serially, blocking everything else while doing so. Recently, many browsers have improved the situation by loading the scripts in parallel, but still executing them in order.

On-demand (or dynamic) script loading has emerged in recent years for a variety of different reasons, most notably the performance improvements to address such concerns. It is desired by many different scenarios to be able to download scripts to a page completely independently of the loading of the rest of a page's resources, or even well after a page has finished loading ("on-demand").

Recent additions to HTML spec such as "defer" and "async" were intended to address the use-case for parser-inserted script elements, but their behaviors have been unhelpful for the on-demand loading use-case (script-inserted script elements).

Thus, script loaders (like LABjs) were developed to give page authors an easy way to specify one or more scripts to load (regardless of when in the life-time of the page that loading is requested), and for as many of them as possible to load in parallel, and for those scripts to be loadable from any local or remote domain location, and for the script loader to be able to control the execution order (if the usage of the script loader's API expresses the need to) to preserve dependencies between the scripts.

Sometimes, dynamic script loading is used to load totally independent scripts, and thus "as fast as possible" execution is desired. Other times (and possibly more frequently), multiple scripts are loaded with some dependencies among them, requiring them to execute in a certain order.

What is needed is some facility by which a script loader can express that dynamic script loading either does or does not need execution order preserved among the queue of requested script loadings.

Current Limitations

Unfortunately, browser behavior around script-inserted script elements and their loading and execution behavior is splintered. There are, at present, two main camps of behavior:

a) in IE and Webkit (including Chrome), the default behavior for script-inserted script elements is for them all to execute in "as fast as possible" mode, meaning there's no guarantee about ordering. This effectively makes on-demand (dynamic) script loading impossible to work in parallel-mode if the resources in question have dependencies -- the only straight-forward way to handle things is to load each file and execute, serially, losing the parallel loading performance benefits.

I'm not sure if this is the case.

IE will start fetching resources without execution simply by setting the src attribute. Scripts will not execute, however, until they are added to the document. Since IE supports the readyState attribute and readystatechange event, it's possible to load scripts in parallel and execute them in order without resorting to invalid type attribute hack-ery.

script= document.createElement("SCRIPT"); 
script.onreadystatechange= function(){ 
    if(this.readyState == 'loaded')
          /* 
             Network fetch is now complete, following
             triggers synchronous execution.  
          */
          document.body.appendChild(this); 
    else if(this.readyState == "complete")
          this.onreadystatechange= null; 
}
script.src= "foo.js"; 
/* Network fetching begins now */

I've experimented with such things heavily in the past, and I'm fairly suspicious that this is not reliable across various versions of IE (6-9), nor does it work reliably the same depending on if an element is already in the browser cache or not. I'm not 100% certain that what you suggest is flawed, but I have a strong hunch that it can be shown to have some holes in it for IE loading. I will try to create some test cases to prove or disprove.

When loading a script that is in the cache, IE requires the event handler be installed prior to setting the `src` attribute. Otherwise the element's transition to the "loaded" state will be missed. IE routinely "skips" ready state transitions when cached scripts are immediately inserted into the DOM so suspicion is understandable; however, delayed insertion works around this "problem". The script above has been tested in IE6-8 with scripts in a variety of caching states and found to work correctly.

Moreover, you're suggesting something that is AFAIK only IE right now, and definitely appears to not be standardized. In the same way that I'm hesitant to continue pinning loading behavior on hacks like the invalid type attribute value, I wouldn't want to approach loading with this technique (even if it were proven reliable on all IE's under all caching circumstances) unless it was a serious proposal to the W3C to have this technique be the new standard (as opposed to the current proposal, as implemented by Mozilla and Webkit now).

Update: the behavior *is* in fact in the spec as a suggestion (aka, with "may" instead of "should"). The wording in question is located in HTML5 4.3.1, "Running A Script" algorithm step, 12, which states:

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.

Getify 19:32, 23 December 2010 (UTC)

I think you're right: Only IE impelements this event and property on script elements. The readystatechange event, however, is widely understood due to the XmlHttpRequest implementation and `readyState` property has been added to the `document` in WebKit, Mozilla and IE.

That having been said, I think this is at least a viable candidate for "Alternate Proposals" and deserves to be listed as such. As always, I welcome input from the community on all the ideas presented here, including this one. But, since the main proposal (async=false) is already being implemented by at least two major browsers, I'm not sure if the tide will be changed or not.

Also, the suggestion of handling the "preload" and deferred execution via the script tag's `readyState` property is very similar in spirit to the suggestion that was made to explicitly support preloading into cache without execution. As I said, either of those two approaches might work, but they both put more of the onus of complexity in managing multiple dependencies on the high-level code (author, script loader, etc). I'm more in favor of this functionality being explicitly built-in natively into the queues. As always, I welcome comments to any side of this discussion.

Getify 18:35, 15 December 2010 (UTC)


b) in Gecko and Opera, the default behavior for script-inserted script elements is for them all to load in parallel, but execute serially in insertion order. Technically, this makes it impossible to dynamically load independent script elements and have them execute in "as fast as possible" mode.

As described above, both behaviors are desirable under different circumstances, but each of the two camps provides only one behavior, and no way to straightforwardly achieve the other. This obviously creates a big nightmare interoperability-wise when trying to provide a general script loader cross-browser.

Since it is observed that the behavior in (a) is more detrimental (race-condition wise) for the case where dependencies exist, script loaders like LABjs had to find some way around this problem while still attempting the best possible parallel loading performance. However, the trick used is hacky and not completely reliable -- yet it is the best way to solve the use-case in IE and Webkit.

For Gecko/Opera, the concession was just (silently) made that the lesser-common use case of "as fast as possible" wasn't possible, but degraded fine to "insertion order execution", while keeping the parallel loading benefits.

Gecko's strict execution order creates dependencies between scripts which are unrelated and prevents the ability mitigate the impact of server outages / network latency. This is exacerbated by the fact that Gecko, unlike IE and WebKit, provides no mechanism to remove scripts from the execution queue.

Consider the case where JSONP is used to provide auto-completion. The user types "hello" and the JSONP request is made; however, the "currently" loading script is out-of-date as soon as the user modifies his search to "hello world". Gecko's algorithm requires the out-of-date request load and execute before the second request.

Recently, Gecko landed a patch to the trunk that stopped Gecko's existing behavior of preserving execution order, making script-inserted script elements now execute in "as fast as possible" mode similiar to IE/Webkit. Unfortunately, the "workaround" used in IE/Webkit (described in the next section) for dealing with parallel loading and execution-order dependencies does not work in Gecko, which means presently the use-case in question is now broken in Gecko trunk/FF4 nightlies.

Moreover, Webkit recently landed a patch to the trunk that stopped Webkit's non-standard but long-held behavior (also used in the "workaround" described in the next section) of loading into cache script resources with an unrecognized "type" value, but silently not executing them. This behvaior (while hacky) is central to being able to address the use-case in question in Webkit, so at present, Webkit nightlies are now also entirely broken on the use-case (though in a different way than Gecko).

Both the Gecko change and the Webkit change are well-intentioned, as they are bringing the respective browser more in line with the HTML spec. However, what's really been demonstrated is that the HTML spec is not properly handling this use-case, and so the goal is not to address the browser issues raised with more awkward hacks, but to address the shortcomings of the spec first, and then encourage all browsers to adhere to such.

Considering Internet Explorer's readyState implementation provides a means of both parallel loading and preserving execution order can be achieved, perhaps its "readyState" property and onreadystatechange event should be included.

Current Usage and Workarounds

To work around the limitation in IE/Webkit(prior to the above noted patch) of not being able to rely on script order execution for parallel loaded scripts, a "preloading" trick was developed. This trick relied on non-standard (but long-held) behavior in these browsers that a script-inserted script element with an unrecognized "type" value (such as "script/cache") would be fetched/loaded, but would not execute. This had the effect of loading the resource into cache, and then firing the "load" handlers to let the page know when the resource was completely in cache.

Assuming that the resource was served with proper caching headers, and was in fact in the cache, it could then be executed (nearly) immediately when it was the proper execution order time by re-requesting the same resource via another script-inserted script element with the proper "text/javascript" type value, pulling the resource from the cache and executing it, without another server round-trip.

Of course, the assumption of proper cache headers is a huge one, and not at all reliable. Some recent estimates by performance optimization specialists have suggested as much as 70% of scripts across the internet are not served with proper caching headers, which means that such scripts would be completely ineffective if loaded using this (or a similar) technique. The script resource would end up being loaded completely a second-time, and the "near immediate" execution would obviously be false, and thus race conditions would ensue.

It's important to note at this point that the new <link rel=prefetch> facility has been suggested as a better workaround, but it suffers the same ill-fated assumption of script cacheability. Still others have suggested "new Image().src=..." or the <object> preloading trick suggested by Stoyan Stefanov. Again, these tricks unwisely assume cacheability, for the "preloading" trick to solve the use-case in question.

At Risk

Currently, there are several large/popular web sites which are either currently (or who intend to soon) use LABjs in such a way as to run afoul of the new Gecko and Webkit behavioral changes with LABjs failing to operate properly. It's important to note that the problems of race conditions can be subtle and hard to detect, and so merely loading up such sites and failing to observe overt failure is not sufficient.

Sites which are known to have LABjs loading techniques in place with currently broken (or susceptible to such breakage in the near future) behavior are:

Rather than getting hung up in the syntax of usage in LABjs that is or is not going to break, it's best to just think of the problem this way:

Does a site need to load more than one script, at least one of which comes from a remote domain location (like a CDN), and for which among the scripts there's at least one execution-order dependency among them? If so, then that site is susceptible to the current/future breakage if the HTML spec (and browsers) do not address this use case.

A common example (in use on many sites) of such might be loading:

  • jQuery from the CDN
  • jQuery-UI from the CDN
  • plugins and usage code in one or more local files
  • Google Analytics from the Google domain

Note that the emergence of popular script frameworks and their hosting on public CDN's is leading to more and more sites loading scripts from both local and remote locations, and also to loading more files that have dependencies (rather than the practice of concat'ing all files into one file to avoid dependency issues).

Any site which fits a profile like the above, and which might currently (many do), or in the future want to, use a script loader to improve their loading performance, will fail to achieve what they want cross-browser and in a performant way, if the HTML spec (and browsers) do not address the use case.

Benefits

The benefits of addressing both behaviors directly (without "preloading" tricks and bad assumption reliance) have been implied in the above discussion, but in short are:

a) clear and simplified code for script loaders, which leads to easier use by authors of more pages, which in turn leads to better web performance (as demonstrated clearly by intelligent script loading techniques as compared to just simple <script> tags in HTML markup)

b) full access to either/both of the execution-order behaviors (as the author sees fit), regardless of browser

c) avoiding reliance on bad assumptions (like cacheability) as a sufficient way to address the use-case

External Discussions of this Feature

Proposed Solutions

My Solution

The current proposal most well supported from the email discussion thread, and the one which I feel makes most sense, is described here.

The HTML spec already defines the "async" attribute for parser-inserted script tags, which when set to "true", changes their execution order behavior to be like script-inserted script-elements (in IE/Webkit), which is that they load in parallel and execute "as fast as possible" (ostensibly because the author is expressing no dependencies between multiple such "async"-marked scripts). Parser-inserted script elements without "async" (or with it set to false) behave as before and expected, which is that they load in parallel but execute in order.

However, the HTML spec does not define the "async" property (or any such behavior) for script-inserted script nodes (such as those created by a script loader). Instead, the spec implies that "async=true" like behavior is always true for such script-inserted script elements.

What is proposed is:

a) Script-inserted script elements should have (and respect the value of) an "async" property which is basically identical to the "async" attribute for parser-inserted script elements. That is, script elements with the "async" property set to "true" will behave accordingly, as will script elements with the "async" property set to false.

Essentially, the proposal is to mirror the "async" attribute behavior of parser-inserted script elements as an "async" property on script-inserted script elements. This has the benefit of using an existing facility and extending it (from current spec) in a way that is sensisble and symmetric with its current definition.

The scriptgroup or `readyState` alternate proposals allow for dependency management to be done in high-level code. The singular syncrhonized execution queue proposed here introduces artificial dependencies, undermining some of the benefits dynamic script loading provides.

It's important to note that `async=false` scripts will load in a separate queue from `async=true` scripts. It's not that all scripts will now be forced into this insertion-order-execution queue. An author (or script loader) can chose which queue to put a script loading into based on the needs of the page and the resource. If I have a few scripts that are unrelated to each other and the main page (like Google Analytics, social sharing buttons, etc), I'll load those via `async=true`. For other scripts, where order matters for dependency sake, I'll use `async=false`. I think having both those queues available to high-level code is enough control over loading to serve the majority of use-cases pretty well.

If at any point these services begin dynamically loading `async=false` scripts of their own, they will begin blocking. This can lead to unexpected behavior of existing code as third-party libraries and services change. It would seem the specification should provide a means by which this blocking can be isolated to dependencies defined by the author and ordered execution not introduce conditions that allow unrelated libraries to affect this.

b) Furthermore, to aid in "feature-detection" of such new behavior, the proposal is to have the default value for the "async" property of script-inserted script elements be "true" (and of course the associated behavior thereof).

There are two major benefits to (b). One is that it provides a way to feature test such new behavior by not just looking for the existence of the "async" property on script elements, but specifically that the default value is "true" (which is opposite of what it would currently/normally be). Secondly, defaulting to "async=true" behavior for script-inserted script elements would preserve the default behavior of IE and Webkit, meaning there'd be less of a chance of breaking existing web content in either of those two browsers.

It's also important to note that there is no implied or requested effect or dependency between script-inserted script elements and parser-inserted script elements -- the two types of scripts would load and execute in entirely separate behavioral sandboxes.

Proposal Amendment: Need Attribute/Property Semantic Inconsistency

In order to achieve consistency between the semantics of how the parser-inserted script element's `async` attribute and the script-inserted script element's `async` property behave, a slight change needs to be made to the value parsing/interpretation for parser-inserted script elements' `async` attribute.

If a parser-inserted script element has the `async` attribute present, but its value is exactly "false" (or any capitalization thereof), the script element should behave EXACTLY as if no `async` attribute were present.

If the attribute is present and either has no value, or has any other value except "false" (for instance, "true" or "async"), then it should be interpreted to be turning on "async" for that element.

Because it is highly unlikely (and indeed, quite irrational) that any existing web content is doing `async="false"` in HTML markup and yet still expecting "async" behavior to be turned on, it is pretty unlikely that this minor interpretation change will cause backwards-compatibility issues. Getify 03:05, 22 December 2010 (UTC)

Proposal Amendment: Events

In addition to standardizing how scripts load and execute, it's also important to standardize the load and error events, and under what circumstances they fire, etc. Without reliable load/error events, the main proposal is not reliable and is thus unhelpful for the use-case.

A script-inserted script element must fire the "load" event only once, when:

a) immediately after the resource finishes loading, but before parsing/execution, AND

b) the script element loads any non-empty content (that is, it was a successful HTTP request), AND

c) the script element has either loaded from the remote location, or is loaded from the brower cache

Specifically, the script "load" event must come immediately between the script element finishing loading and the script element being parsed/executed. If the script resource successfully loads, nothing should interrupt the sequence of fininshing loading, the "load" event firing, and the script being parsed/executed.

A script-inserted script element must fire the "error" event only once, when:

a) immediately after a loading failure for the resource is detected (that is, an HTTP error received, such as 404, 500, 503, etc).

Either the "load" or "error" event, but not both, will fire, and only once, for every script-inserted script element.

NOTE: "load" and "error" events on script-inserted script elements must fire synchronously to ensure event reliability.

A case may be made for adding the "load" and "error" events as described here to parser-inserted script elements as well.

Processing Model

Script inserted script elements will have an "async" property that defaults to "true". If the author does not change the value, all such requested script loadings will be in their own "queue" and will default to "as fast as possible" execution behavior. For any script elements that the author sets "async=false", those scripts will load in a separate "queue", and will execute in insertion order only. Again, these two "queues" will operate strictly independent of each other.

Processing Model Details (without the event amendment)

When a script element node is created, if it is being flagged as parser-inserted, set its force-async flag to false. Otherwise, set its force-async flag to true. (Note that createContextualFragment, innerHTML and XSLTProcessor::transformToFragment-created scripts are not flagged as parser-inserted.) This flag setting happens before any attributes (even parser-set ones) are set on the node.

When a previously-created script element node loses its parser-insertedness, if the element doesn't have the async content attribute, set the force-async flag to true and false otherwise.

When a script element node obtains the async content attribute (via setAttribute, setAttributeNode, setAttributeNS, by the fragment parser or the XSLTProcessor adding the attribute, etc.), set the force-async flag to false. (Note that calling removeAttribute("async") doesn't modify the force-async flag.)

The async IDL attribute must behave as follows:

  • Upon setting, set the force-async flag to false and then reflect the async content attribute.
  • Upon getting, if the force-async flag is true, return true. Otherwise, reflect the async content attribute.

In step 13. of http://www.whatwg.org/specs/web-apps/current-work/#running-a-script before the case "If the element has a src attribute" add a case: If the script has a src attribute and the async IDL property getter returns false, The element must be added to the queue of ordered script-inserted external scripts of the Document of the script element at the time the running a script algorithm started.

The task that the networking task source places on the task queue once the fetching algorithm has completed must run these steps:

  1. If the queue of ordered script-inserted external scripts is empty or the first script in the queue of ordered script-inserted external scripts has not been fetched yet, abort these steps.
  2. Execute the first script in the queue of ordered script-inserted external scripts.
  3. Remove the first script from queue of ordered script-inserted external scripts.
  4. Goto step #1.

Modify step 5 of  http://www.whatwg.org/specs/web-apps/current-work/#the-end to say: Spin the event loop until the set of scripts that will execute as soon as possible is empty and the queue of ordered script-inserted external scripts is empty.

Limitations

The two behaviors being identified solve either the case where no dependencies exist, or where a linear dependency chain exists (and which the script elements can be requested to execute in that order). It is possible that some authors have a more complex non-linear dependency chain that they would like to express. This would obviously require a much more complex API and spec change, and since that use-case has not (yet) surfaced as a particularly main-stream request, I believe it would be overengineering to try to address it with this proposed change set.

In addition, it's been duly noted that it's undesirable (and potentially confusing/problematic) to intentionally build in the inconsistency of having the "async" attribute (for parser-inserted scripts) and the "async" property (for script-inserted scripts) have different default values ("false" for the attribute, "true" for the property).

It's also been a point of discussion whether or not such a spec change has enough "carrot" to entice the browser vendors (namely IE and Webkit) to implement the behavior. Moreover, there's been some concern that if the default value for the "async" property were made to be "false" (like the attribute) to be more consistent and conservative, then it would possibly give the perception to IE and Webkit of "losing" some performance to cut out their default "as fast as possible" behavior.

Alternate Proposals

One early proposal on the email list was to introduce an entirely new property like "ordered" which an author could add to a script-inserted script element to instruct the browser to put it into the queue of execution-order-preserving script loadings. While such a property would address the use case, it introduces another property and thus more complicates the issue. It also fails to address the current spec inconsistency (which is confusing to new comers) that "async" is not a present/respected property in mirror of the attribute of the same name.

Another suggestion was a "waitFor" property that would be added to script elements and would specify a list of one or more DOM id's of other script elements that the current script should "wait for" in terms of execution. Again, this would solve the use case, but in a more complicated way, and there are concerns that it would be too confusing for the normal use-case.

Several suggestions have come in the form of creating explicit "preloading" (similar to <link rel=prefetch>), but as described above, "preloading" to solve this use case is a non-ideal hack and highly susceptible to breakage if the script fails to be sent with proper caching headers.

It's also been suggested that since this type of behavior is somewhat complicated, it may be better to intentionally obfuscate or complicate any such facility in the HTML, so as to make the barrier-to-entry rather high and force users to know what they are doing before doing it.

It's been suggested that "defer" already preserves execution order. However, "defer" is only defined for parser-inserted scripts, and thus is not applicable to the use-case in question from an on-demand point of view. Also, "defer" scripts explicitly way for DOMContentLoaded, even if they're ready to execute sooner. So this is less than desired.

Yet another proposal is a "document.executeScripts()" API, where an author can specify multiple sets of scripts that can load in parallel and it will enforce their execution order. A variation on that same idea was to use the "importScripts" from the Web Workers spec, however "importScripts" is synchronous (undesirable performance wise, obviously). The main downside (besides extra API complication) to "document.executeScripts()" is that there seem to be quite a few script execution properties/behaviors (including "document.currentScript" and charset override) which would have to be duplicated into this API facility.

"Script Group" element

One recent alternate proposal bears some special consideration, as it seems like it might be a decent option (although certainly more of a radical change for the spec and for browsers to implement). But it has the appearance of being pretty straightforward and semantic for authors to use, perhaps even more so than using "async".

The proposal is to create a new HTML element, called perhaps <scriptGroup>, <collection>, etc. Specifically, this element must be able to be inserted wherever a <script> element can currently be inserted. The "script group" element is intended to signify that all script elements added to it must perserve insertion execution order. This element wouldn't have much (but still some, explained in a moment) meaning in a parser-inserted context, since parser-inserted scripts already preserve order among themselves.

An advantage of the "script group" element would be to give a direct and easy way to attach event listeners ("load" and "error") to the entire group, rather than having to internally track events for each element if all you care about is the final "load" event, for instance. In the case of event handling, the "script group" element would have perhaps some benefit even in parser-inserted (markup) context.

The element would need to have an "id" property, and possibly attributes for "load" and "error" events.

A variation on how to look at this proposal is that a "script group" element could have an attribute/property on it (perhaps called "ordered") which would allow the group to either be order preserved or not. This would make the "script group" element much more useful in the parser-inserted context, as it would sort of be a shortcut to setting "async=true" on all the child script elements.

The "script group" element might look like this:

 <scriptGroup id="group1">
   <script src="foo.js"></script>
   <script src="bar.js"></script>
   <script>
     somethingInline();
   </script>
 </scriptGroup>

Or, with the "ordered" attribute to explicitly control ordering behavior for the group:

 <scriptGroup id="group1" ordered="true">
   <script src="foo.js"></script>
   <script src="bar.js"></script>
   <script>
     somethingInline();
   </script>
 </scriptGroup>
 <scriptGroup id="group2" ordered="false" onload="alldone();">
   <script src="baz.js"></script>
   <script src="far.js"></script>
   <script src="zab.js"></script>
 </scriptGroup>

While the "script group" element is definitely more complicated to define and implement, it does have some semantic advantages for authors, and it also would significantly reduce the internal complexity of script loaders like LABjs. It would give authors (either directly or through script loaders) the flexibility to group scripts together into one of the two aforementioned behaviors (execution "as fast as possible" or "in insertion order"), and to easily access both behaviors in the same page.

Explicit support for "text/cache" MIME type behavior

Yet another alternate proposal has been suggested in the Talk:Dynamic_Script_Execution_Order thread.

Essentially, the proposal is that <script> tags directly support being declared with a `type` value of "text/cache". The behavior would be that the script resource is fetched, as the load events fired, as normal, but that the script itself would not be executed. Then later, at the desired time, the script in that element could be executed by changing the `type` value from "text/cache" to "text/javascript".

The browser would probably need to keep track (via some internal flag) that the script node is only ever executed once. Also, it's an open question if such a "cached" script node should be able to have its script content modified before execution via the `text` property.

And does the onload event fire again after the script executes?
Or only when it's loaded?
Both seem to violate current `onload` semantics. Serverherder 20:27, 15 December 2010 (UTC)

As far as I'm concerned, I think the "load" event should happen immediately after a script loads, but before it executes, as I spell out in the Events proposal ammendment section. Getify 20:33, 15 December 2010 (UTC)

readyState "preloading"

Per the suggestion/comment added to the above Current Limitations section, it's been suggested that IE's current behavior with respect to the `readyState` property can adequately serve the use-case in question. This alternate proposal is very similar in spirit to the previous section on explicit support for "text/cache" mime-type value.

The idea is that IE will begin fetching into the cache a script resource as soon as the `src` attribute of the dynamic script element is set. But IE will not execute the script (even if it finishes loading) until the script element is added to the DOM. However, the `readyState` property of the element, combined with the `onreadystatechange` handler listening for changes, can detect when the script has finished loading, by receiving the "loaded" value.

In this way, a script loader could "preload" a set of scripts all in parallel, but control their execution order by delaying the appending of the element to the DOM until execution is desired.

Update Getify 14:10, 20 December 2010 (UTC)

HTML Spec, 4.3.1, 'Running a script' algorithm

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.

This seems to be the spec stating that browsers may do what IE's behavior on scripts does, which is to start fetching the resource once the `src` property is set, but not execute it until the script element is inserted into the DOM.

It would seem then that browsers could/should be petitioned to consider implementing this suggestion, perhaps taking the `readyState` implementation from IE as guidance. While I'm still not sure that this would be my preferred method of solving the overall use-case in discussion on this page, it would be an option for such, and it would also help other use-cases, such as those being discussed in this W3C public-html thread:

Need to: "preload" CSS and JS

Do nothing and encourage vendors implement the existing suggestion

As noted by Getify above, the spec all ready makes the suggestion that a script's SRC attribute be fetched immediately upon assignment.

HTML Spec, 4.3.1, 'Running a script' algorithm

While not explicit, it seems the specification all ready deals with the use-case this is hoping to achieve: It makes parallel loading scripts optional. Execution order can all ready be handled by loaders using onload-chaining. Therefore, parallel loading can be achieved, but only in browsers that have chosen to implement the suggestion.

Given this, the discussion can shift to:

  1. Should the suggestion be changed to a mandate?
  2. Should the specification include a way to force parallel loading?
  3. Should we be petitioning vendors, not the the W3C, to implement the existing suggestion?