[whatwg] Script preloading

First, let's get down to use cases. Kyle did a great job of describing 
some key use cases:

On Wed, 10 Jul 2013, Kyle Simpson wrote:
>
> [Use-case Q:] I am dynamically loading one of those social widgets that, 
> upon load, automatically scans a page and renders social buttons. I need 
> to be able to preload that script so it's ready to execute, but decide 
> when I want it to run against the page. I don't want to wait for true 
> on-demand loading, like when my user clicks a button, because of the 
> loading delay that will be visible to the user, so I want to pre-load 
> that script and have it waiting, ready at a moment's notice to say "it's 
> ok to execute, do it now! now! now!".

> [Use-case S:] One CMS plugin wants to load "A.js" and "B.js", where B 
> relies on A. Both need to load in parallel (for performance), but A must 
> execute before B executes. I don't control A and B, so changing them is 
> not an option. This CMS plugin [wants] to wait for some 
> user-interaction, such as a button click, before executing the code. We 
> don't want there to be any big network-loading delay visible to the user 
> between their click of the button and the running of that plugin's code.
> 
> Another CMS plugin on this same page wants to load "A.js", "C.js", and 
> "D.js". This plugin doesn't know or care that the other plugin also 
> requests "A.js". It doesn't know if there is a script element in the 
> page requesting it or not, and it doesn't want to looking for it. It 
> just wants to ask for A as a pre-requisite to C and D. But C and D have 
> no dependency on each other, only a shared dependency on A. C and D 
> should be free to run ASAP (in whichever order), assuming that A has 
> already run [once] some user-interaction that initiates the load of A, 
> C, and D. This user interaction may be before the other plugin requested 
> A, or after it requested A.
>
> "A.js" can be requested relatively (via a <base> tag or just relative to 
> document root) or absolutely, or it might be requested with the 
> leading-// protocol-relative from, taking on whatever http or https 
> protocol the page has, whereas other references to it may specify the 
> prototcol.
>
> These plugins can't guarantee what ID's or classes they use are reliably 
> unique without undue processing burden.

[I've trimmed the text Kyle wrote here, but also implicit in his 
description, as I understood it, was that A.js should only run once even 
if both plugins tried to load it.]

> [Use-case T:] I have two different calendar widgets. I want to pop one 
> of them up when a user clicks a button. The user may never click the 
> button, in which case I don't want the calendar widget to have ever 
> executed to render. [...]
> 
> It'd be nice if both calendar widgets were built sensibly so that 
> loading the code didn't automatically render. One of them IS, the other 
> is unfortunately mis-behaving, and it will render itself as soon as its 
> code is run. [...]
> 
> Furthermore, these two widgets are not "equal". Perhaps one is better 
> for smaller browser window sizes, and the other is better for larger 
> browser windows. [...]
> 
> Regardless, the point is, there's run-time conditions which are going to 
> determine if I want to execute calendar widget A or B, or maybe I never 
> execute either. But I want them both preloaded and ready to go, just in 
> case, so that if the user DOES need one, it's free and ready to execute 
> with nearly no delay, instead of having to wait to request as I would 
> with only on-demand techniques.

> [Use-case U:] I have a set of script "A.js", "B.js", and "C.js". B 
> relies on A, and C relies on B. So they need to execute strictly in that 
> order. [Now], imagine they progressively render different parts of a 
> widget. [...] I only want to execute A, B and C once all 3 are preloaded 
> and ready to go. It's [...] about minimizing delays between them, for 
> performance PERCEPTION.
>
> [For example, one of them might start playing a video, and another might 
> introduce the <canvas> slides for that video. You want all of the 
> relevant scripts to be run at once, so there's no point where the page 
> has a <video> element but doesn't have the <canvas>.]

On Thu, 11 Jul 2013, Kyle Simpson wrote:
> 
> [Use-case V:] you have a string of scripts ("A.js", "B.js", and "C.js") 
> loading which constitute a dependency chain. A must run before B, which 
> must run before C. However, if you detect an error in loading, you stop 
> the rest of the executions (and preferably loading too!), since clearly 
> dependencies will fail for further scripts, and the errors will just 
> unnecessarily clutter the developer console log making it harder to 
> debug.

> [Use-case W:] some developers have even requested to be able to stop the 
> chain and prevent further executions if the script loads, but there's 
> some compile-time syntax error or run-time error that happens during the 
> execution. For them, it's not enough for B to simply finish loading 
> successfully, but that it must fully execute without error.

On Sun, 14 Jul 2013, Kornel Lesiński wrote (trimmed):
> 
> [Use-case X:] not all dependencies are JS files, e.g. authors use 
> plugins to load template files, JSON, images, etc.
> 
> [Use-case Y:] not all dependencies are usefully satisfied immediately 
> after their JS file is loaded, e.g. some libraries may need asynchronous 
> initialization.
>
> [Use-case Z:] Another common kind of dependency scripts have is presence 
> of certain element in the DOM, e.g. `dropdown-menu.js` may require `<nav 
> id="menu">` to be in the document _and_ have its content fully parsed 
> before the script can run.

These use cases are all very helpful. Thanks.

Jake also mentioned these requirements:

| - Provides an adoption path for browsers that don't support the new
|   feature (happy for the fallback to be blocking document-order 
|   execution)
| - Is discoverable by pre-parsers (so async=false and old-IE's 
|   readystate methods aren't enough)

And Kyle mentioned this scenario that we need to handle as well (not
strictly a use case, more a variant on the above use cases):

> I want to preload a script which is hosted somewhere that I don't
> control caching headers, and to my dismay, I discover that they are
> serving the script with incorrect/busted/missing caching headers.



On Tue, 9 Jul 2013, Kyle Simpson wrote:
> 
> I think it's important to note that the primary motivation here is 
> performance.

The primary motivation is running scripts. Performance is just something 
we want all features to have, like security, accessibility, usability, 
support for all locales, etc.

Incidentally, I don't think the use case is or should be "make it possible 
to write a performant script loading library". I think the use case should 
be more like "make it unnecessary to ever write a script loading library".


On Tue, 9 Jul 2013, Kyle Simpson wrote:
> 
> But even if we standardized a third option, and I had to change LABjs, 
> that would be FAR BETTER in my mind than never addressing this use case 
> at all, especially in light of IE11 sort of retreating on this topic 
> (either intentionally or not).

I think the ideal solution would obsolete LABjs, no?


On Thu, 11 Jul 2013, Jake Archibald wrote:
> 
> link[rel=subresource] is the right solution for preloading [...]

I don't know about that. It makes for rather verbose script just to load 
scripts, which seems like a loss. Surely script loading should just happen 
without having to write extra script to do it.


All of the solutions proposed so far either entirely fail to handle some 
of the use cases, or only support them to the extent that they provide 
some tools with which you can implement scripts that handle the use cases.

IMHO, if you have to write a script to solve use cases like these, you 
haven't really solved the use cases. It seems that the opportunity we have 
here is to provide a feature or set of features that addresses these use 
cases directly, so that anyone can use them without much work.


On Thu, 11 Jul 2013, Yoav Weiss wrote:
> 
> I've recently contemplated the slightly related issue of adding the 
> "media" attribute to <script>, for declarative loading of scripts that 
> are only relevant to some viewports [1] While it may complicate certain 
> things (e.g. execution when media conditions change, dependencies), I 
> believe it's worth while to give it some thought, as it'd enable 
> preloaders to fetch these scripts as soon as possible, in case they are 
> needed.

How common are media-specific scripts?


On Mon, 15 Jul 2013, Bruno Racineux wrote:
> 
> Wouldn't browsers be able to store "pre-parsed/compiled' scripts in a 
> separate "byte code" cache, with scripts promoted to the sticky cache 
> based on their access frequency (up to cache expiration)? Say similarly 
> to the way Fusion Drives or Seagate Adaptive Memory SSHDs work.
> 
> i.e. Why do we have to keep re-parsing and re-evaluating the very same 
> scripts, especially CDN libraries and social apis largely shared among 
> websites, over and over?

Fundamentally the problem is that they're often not exactly the very same 
scripts, so the wins aren't as great as you might think.

But there's nothing stopping browsers from doing this today. File bugs 
with browsers. :-)


> If javascript can't be as truly fast a native apps: 
> http://sealedabstract.com/rants/why-mobile-web-apps-are-slow/ It seems 
> that for frequently visited sites and frequently accessed libraries 
> across websites, the whole parse/evaluation time, could be cut 
> 'partially', yet very significantly, just like opcode does for php.

The article you cite basically answers your question: the problem isn't 
downloading and compiling the scripts, the problem is garbage collection 
and other intrinsic aspects of the JavaScript language and the rest of the 
Web platform.


On Tue, 9 Jul 2013, Garrett Smith wrote:
> 
> Why limit "depends" to be used by only scripts to refer only to other 
> scripts?
> 
> If you put <link depends=idref> on style then stylesheet evaluation 
> could be deferred, too.
> 
> I explained some cases about why this is desirable here: 
> http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-February/018435.html

I responded to that e-mail here:

http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-February/018542.html


On Tue, 9 Jul 2013, Bruno Racineux wrote:
>
> Why not simply load all such scripts early in the <head> with 'defer', 
> which preserves the dependency order as determined by your app. Using 
> 'defer' in head scripts is actually a very good way to preserve script 
> order with non-blocking scripts. And by loading the scripts very early 
> in the <head>, the possibility of a incurred significant delay of 
> DOMContentLoaded, for an eventual large script not yet downloaded, is 
> minimal to none.

This doesn't seem like it handles all the use cases (e.g. T and U).


> I would also strongly favor restoring the previous spec portion of 
> 'defer' which allow to have defer on inline script blocks (i.e. if the 
> src attribute is not present). I don't know why this html4 functionality 
> was removed from html5?

Well, primarily because basically nobody implemented it, but also, because 
it's not clear what the point is. Why would you need it?


> I see use cases which would give much more flexibility with dependencies 
> (such as putting jquery on 'defer' in the head with inline 'defer' 
> jquery functions) and possibly even improving performance significantly.

Why not just wait for jQuery to say it's loaded, and then run whatever 
scripts depend on jQuery?


> Back to the initial question from my rant. While there is a use case for 
> not downloading or executing scripts until needed, this can somewhat 
> already be dealt with using ajax or the post-eval method google uses. Or 
> perhaps this script execution hold could be an ajax only solution, with 
> a no-execute-yet property and a pure javascript behavior, rather than 
> also implicate DOM attributes into this.

Such solutions are somewhat unsatisfactory. A dedicated solution would be 
easier to use and less confusing.


On Wed, 10 Jul 2013, Bruno Racineux wrote:
>
> I think the very idea of having the concept of 'dependency' attributes 
> and relying or asking to the browser to handle it through markup is 
> misguided.
>
> This is somehow throwing the entire (or a portion of) dependency 
> information of your platform, asking the browser to deal with it. I can 
> already imagine bloated pages with an entire set of script markup, 
> bigger than that of the page itself. That's not very good for 
> performance.

I don't understand. Can you elaborate on this? Why is it misguided?


> I think Kyle has given more than enough substantiated arguments in: 
> http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2013-July/039981.html 
> not to have me repeat them.

These use cases don't suggest that the browser shouldn't solve them, on 
the contrary.


On Mon, 15 Jul 2013, Kornel Lesiński wrote:
> 
> ES6 modules[1] have a script loader API[2].
> 
> That API is pretty powerful to the point it can emulate other script 
> loaders, load files that are not ES6 modules, and even load text files 
> that aren't JS (intended for compilation of coffeescript-like languages, 
> but could be abused for anything):
> 
> https://gist.github.com/wycats/51c96e3adcdb3a68cbc3#using-existing-libraries-as-modules
> 
> There's a very high overlap between module dependencies and <script 
> dependencies> proposal. I think at very least it would be useful to 
> define <script dependencies> in terms of ES6 modules, or even abandon 
> markup solution to avoid duplicating features.
> 
> ES6 modules however do not solve the performance problem. In fact they 
> would benefit from UA having a list of all dependencies up front 
> (otherwise file's dependencies can only be discovered after that file is 
> loaded, which costs as many RTTs as the height of the dependency tree).
> 
> So I think that eventually ES6 modules + link[rel=subresource] could be 
> the answer. The <link> would expose URLs to (pre)load for performance, 
> but modules would handle actual loading/execution for flexibility and 
> reliability.

The ES6 module doesn't address some of the use cases above, as far as I 
can tell (e.g. Q, U) and require a lot of work to handle some of the 
others. But it seems important that anything we add to HTML be designed to 
work with ES6 modules on the long run.

(I'm assuming the current state of this is:

   http://wiki.ecmascript.org/doku.php?id=harmony:modules
   http://wiki.ecmascript.org/doku.php?id=harmony:module_loaders

...but that seems to have changed some since wycats' proposal above.)


On Mon, 22 Jul 2013, Jonas Sicking wrote:
> 
> [...] adding a "noexecute" attribute on the <script> element which 
> causes the script element not to execute when it normally would. Instead 
> it fires the "load" event when the script has been loaded and does 
> nothing more.
> 
> Once the page wants the script to execute, it would call a new 
> .execute() function on the script which would cause the loaded script to 
> execute. If the function is called before the load event has fired, an 
> InvalidStateError exception would be thrown.

This doesn't seem like it would address many of the use cases, at least 
not without a bunch more supporting code. It also doesn't seem like it 
would really coordinate well with ES6 modules (unless there was some way 
to say "by the way, this script implement that module" or something, with 
'import' being able to essentially automatically invoke "execute()").


On Mon, 22 Jul 2013, Kyle Simpson wrote:
> Jonas wrote about JS error conditions:
> > 
> > 1. Failed network request
> > 2. Failed JS compilation
> > 3. Exception thrown from execution
> > 
> > And there are two error reporting mechanisms in play
> > 
> > A. Fire an "error" event on the <script> element.
> > B. Fire the window.onerror callback (like an event, but not exactly the same).
> 
> Agreed. `window.onerror` serves fine case #3. What we don't seem to have 
> as consistent cross-browser behavior, or even terribly well defined in 
> the spec, is #1 and #2, especially #1. Various older browsers had 
> different interpretations as to which network conditions constituted 
> "load complete" or not.

Can you elaborate on what is underdefined about this in the spec?


On Mon, 22 Jul 2013, Jonas Sicking wrote:
> 
> In the noexecute proposal I believe that .execute() would be a 
> synchronous operation. Though we could change it to be an asynchronous 
> operation if that's desired.

It's not clear to me how script execution in the ES6 Module Loader world 
works when it's asynchronous. As in, if you document.appendChild() an 
inline script that has a dependency, does the calling script resume 
executing before the inner script? Or does it block on the network like a 
synchronous XHR?

(I spoke with wycats and he tells me there'll be spec hooks I use in HTML 
to invoke JS' loading logic so that in this case we'd use evalAsync and it 
would throw SyntaxError. Similar hooks will be available to make sure we 
use fetch.spec.whatwg.org for the loading logic.)


On Wed, 10 Jul 2013, Jake Archibald wrote:
> 
> If "dependencies" took a CSS selector it could be:
> 
> <script dependencies=".cms-core" src="cmd-plugin.js"></script>
> 
> Now the number of scripts with class "cms-core" can change between 
> versions of the CMS but the plugin still waits for them all. No ID 
> generation needed.

Using a selector is an interesting idea.

It makes it harder to detect and prevent loops, but not fatally so.


On Wed, 10 Jul 2013, Kyle Simpson wrote:
> 
> Imagine this scenario: I load jquery.js and 4 other plugins. I could 
> either mark the 4 plugin script tags with "depends on jquery", or I 
> could mark the one jquery element as "fullfills these 4 plugins". The 
> latter is simpler because it requires less markup in general.

That's somewhat backwards from normal programming practices, though.



Here's a proposal that attempts to address all the use cases:

High-level overview:

  <script> elements get a new whenneeded="" attribute, which delays the 
  execution of the script until the <script> element's execute() method is 
  called. (This essentially provides the same as the "preload" 
  suggestions.)

  <script> elements also get a new needs="" attribute, which takes a list 
  of URLs. A <script> won't run (even if you call execute()) until all the 
  <script src=""> elements referenced by its needs="" attribute are 
  themselves ready to run. For example:

     <script src="b.js" needs="a.js"></script>
     <script src="a.js" async></script>

 ...will execute a.js when it's ready, and only then execute b.js. "needs" 
 basically implies "async" if its needs aren't met when it first tries to run.

 <script whenneeded="jit"> is a special mode where instead of running once 
 the script's dependencies are met, it additionally waits until all the 
 scripts that depend on _it_ are ready to run. ("Just in time" exection.) 
 (The default is whenneeded=asap, "as soon as possible" exection.)

 You can manually increase or decrease a dependency count on <script> 
 elements by calling incDependencies() and decDependencies().


Details (tersely):

  scripts have a "whenneeded" mode, initially "none", can also be "asap", "jit".
  scripts have a "ready" flag, initially false
  scripts have a "needed" flag, initially false.
  scripts have an "already ran" flag, initially false.
  scripts have an external dependency count, initially zero.
  documents have the following registries:
     url -> script element (script registry)
     script element -> scripts that this script depends on (dependencies)
  when a script element is removed from the document, it's removed from the 
  registries (ES6 module registry also).

  <script> gets attributes:
     whenneeded="": enumerated attribute, "asap" (invalid), "jit", none (missing)
     needs="": space-separated list of URLs
     module="": name of module for ES6

  definitions:
    a script elemen tA "depends" on a script element B with URL C if B is 
      the <script> element registered as the <script> for URL C
    "a script element's dependencies have been met" when:
      - each script element that it depends on also matches this, and
      - there are no entries in the dependency registry for the element 
        that don't correspond to <script>s in the script registry
      - this script is "ready" and
      - this script's external dependency count is zero
    "a script is ready to run" when:
      - it's dependencies have been met, and
      - it's "whenneeded" mode is "asap" or missing,
        or, it's "jit" and all of the scripts that depend on it are
           either ready to run, or not needed

  "prepare a script" modifications:
     immediately before the step that today invokes fetch, add these steps:
      0. look up the resolved url in the document's registry. if there's an 
         entry already, abort these steps otherwise, add this script element 
         with this resolved url
     immediately before the final step, add:
      0. if there's a module="" attribute, add this script as a module
         to the ES6 module registry, and make the logic that imports the
         module call execute() on the <script> block
      0. if the element has a "whenneeded" attribute:
           - set "whenneeded" mode to state of attribute (asap/jit)
      0. if the element has a "needs" attribute, split it on spaces, and
         for each value:
           0. resolve it relative to the element
           0. add an entry to the dependencies registry, this script 
              is dependant on that one
               - use the order of the entries in the needs="" attribute

  "execute a script block" modifications:
     Change the "If the load was successful" branch to do this instead:
      0. mark self as "ready"
      0. check if we need to run
     ...and move the current steps into a separate "run" algorithm.

  "check if we need to run":
    0. if "whenneeded" is not "none" and "needed" is not true, abort
    0. if the script is not ready to run, abort
    0. if the script did not already run:
        0. mark this script as "already ran"
        0. for each script that this one depends on and whose "whenneeded" 
           mode is "jit", check if it needs to run
        0. run
    0. if we're still ready to run:
       for each script that depends on this one, check if it needs to run

  <script> gets a method, execute():
    0. mark as needed
    0. call execute() on each script we depend on  
    0. if we didn't depend on any, check if we need to run

  <script> gets a pair of methods, incDependencies() and decDependencies(), 
  that increase and decrease the dependency count by one, respectively
  decDependencies() throws if called when the count is zero. If 
  decDependencies() is called and it reduces the number to zero,


Here's what the use cases would look like (the function E() returns an 
element with the given attributes):

   Q:
   <script id="social" src="social-button.js" whenneeded></script>
   <button onmouseover="document.scripts.social.execute()"> ... </button>

   S:
   // CMS plugin 1
   var A = E('script', { src: 'A.js', whenneeded: true });
   var B = E('script', { src: 'B.js', needs: 'A.js', whenneeded: true });
   document.body.append(A, B);
   function sawUserInteraction() {
     B.execute();
   };
   // CMS plugin 2
   var A = E('script', { src: 'A.js', whenneeded: true });
   var C = E('script', { src: 'C.js', needs: 'A.js', whenneeded: true });
   var D = E('script', { src: 'D.js', needs: 'A.js', whenneeded: true });
   document.body.append(A, C, D);
   function sawUserInteraction() {
     C.execute();
     D.execute();
   };

   T:
   <script id=calA src="a.js" whenneeded></script>
   <script id=calB src="b.js" whenneeded></script>
   <script>
    function showCalendar(which) {
      if (which == 'a')
        document.scripts.calA.execute();
      else
        document.scripts.calB.execute();
    }
   </script>

   U:
   <script src="A.js" whenneeded=jit></script>
   <script src="B.js" whenneeded=jit needs="A.js"></script>
   <script id=c src="C.js" whenneeded=jit needs="B.js"></script>
   <script>
    // when we need it...
    document.scripts.c.execute();
   </script>

   V:
   <script src="A.js" whenneeded></script>
   <script src="B.js" whenneeded needs="A.js"></script>
   <script id=c src="C.js" whenneeded needs="B.js"></script>
   <script>
    onerror = function (message, source, lineno, colno, error) {
      // report error
    }
    // when we need it...
    document.scripts.c.execute();
   </script>

   W:
   <script src="A.js" whenneeded></script>
   <script src="B.js" whenneeded needs="A.js"></script>
   <script id=c src="C.js" whenneeded needs="B.js"></script>
   <script>
    // when we need it...
    onerror = function (message, source, lineno, colno, error) {
      document.currentScript.incDependencies();
      // since the dependency count is never reduced, the script blocks
      // all the scripts that depend on it forever
    }
    document.scripts.c.execute();
    onerror = null;
   </script>

   X:
   <script src="deps.js" async></script>
   <script src="build.js" needs="deps.js"></script>
   // in deps.js:
   var me = document.currentScript;
   me.incDependencies();
   var image = new Image();
   image.src = 'image.png';
   image.onload = function () {
     me.decDependencies();
   };

   Y: 
   <script src="slowLoad.js" async></script>
   <script src="next.js" needs="slowLoad.js"></script>
   // in slowLoad.js:
   var me = document.currentScript;
   me.incDependencies();
   setTimeout(init, 1000);
   function init() {
     // ok, we're ready
     me.decDependencies();
   }

   Z:
   The easiest way to do that one would just be to put the <script> after 
   the <nav id="menu"> element in the DOM.


Looking at the other requirements:

 - Fallback: the fallback is just to execute the scripts in document 
   order. This doesn't work when "async" is used, unfortunately, but it 
   does work when whenneeded="" is used.

 - Pre-parsers should discover these fine if they're in markup. OBviously, 
   for script-generated scripts, that won't work.

 - As far as caching goes, the headers shouldn't affect this mechanism, so 
   it'll work fine even with broken headers. (Obviously it'll work better 
   across sessions if the headers aren't broken.)


Any comments on this proposal?



On Wed, 10 Jul 2013, Kyle Simpson wrote:
> 
> You know, I keep relying on the fact that the body of work on this topic 
> for almost 3 years ought NOT have to be re-visited every few months when 
> these threads wake from dormancy. I keep hoping that someone who really 
> cares about actually addressing all the concerns, and not just some of 
> them, will do the due dilligence to look at all the previous stuff 
> before criticizing me for not providing enough detail.

Unfortunately, since you are so verbose in your e-mails, it is impractical 
to go back and read everything you've written. Just this brief thread had 
something like 1300 lines from you alone (out of 2500 lines and a dozen 
participants). The reality of this is that we can't remember everything 
you've ever written. Sorry.


> I doubt anyone is going to read this crazy long message and actually 
> read all these, but I'll put them here nonetheless.

I read everything.


> That was way more than I wanted to write, and probably way more than 
> anyone wanted to read.

I encourage you to work on editing. It's possible to express the same 
ideas in much shorter text. For example, use case U above is adequately 
expressed in the one paragraph I edited it down to, but you had it spread 
over many paragraphs. The key is to notice when what you are saying merely 
repeats something you've already said, and to just pick the expression of 
the idea that says it best, and remove the other one. Another example 
would be saying "X doesn't want A, it wants not-A". You don't have to say 
the first part -- if it wants not-A, then it usually obviously doesn't 
want A. So you can just say "X wants not-A". Similarly, if you ever start 
a sentence with "Again" (or indeed "similarly"!), you have a good 
candidate for something being redundant. :-)


On Tue, 9 Jul 2013, Kyle Simpson wrote:
> 
> > The proposals I've seen so far for extending the spec's script 
> > preloading mechanisms fall into two categories:
> > 
> > - provide some more control over the mechanisms already there, e.g. 
> >   firing events at various times, adding attributes to make the script 
> >   loading algorithm work differently, or adding methods to trigger 
> >   particular parts of the algorithm under author control.
> > 
> > - provide a layer above the current algorithm that provides strong 
> >   semantics, but that doesn't have much impact on the loading algorithm 
> >   itself.
> > 
> > I'm very hesitant to do the first of these, because the algorithm is _so_ 
> > complicated that adding anything else to it is just going to result in 
> > bugs in browsers. There comes a point where an algorithm just becomes so 
> > hard to accurately test that it's a lost cause.
> 
> Can you please elaborate on how either of the two prominent proposals 
> that Nicholas Zakas and I detailed years ago here are insufficient in 
> that they fall into your first category?
> 
> http://wiki.whatwg.org/wiki/Script_Execution_Control

Well they don't address the use cases directly. They provide APIs that can 
then be used to write script to address the use cases.


> How does standardizing that suggestion (turning it from a MAY into a 
> MUST) further complicate the loading algorithm, especially since there's 
> over a decade of proof in IE that it works and works fine, and it's 
> already detailed as a suggestion in the spec?

I disagree with the premise of the question (that IE proves it works 
fine), since IE didn't implement exactly what the spec wrote plus that, it 
implemented variants that are hard to distinguish. But in any case, that 
didn't address the use cases directly either.


> In neither case do I see how this unduly complicates the loading 
> algorithm? It ONLY pauses it between load and execute, under certain 
> conditions. It doesn't change any of the order of the algorithm.

One can complicate by adding as well as reordering. The question is 
whether it's "unduly". In practice my proposal above is much more 
complicated than that, but it does so to address the use cases.


> For instance, content-management-systems (through plugins) load 
> different items into a page independently of other plugins. Those 
> plugins have no knowledge of the complete content/markup-structure of 
> the page, and so they would be unable to reliably create <script> 
> elements with unique ID's, without just guessing by creating really 
> obscure ID's. Not perfect/perfectly reliable, and certainly ugly and 
> more work for them to do. Every plugin would have to come up with its 
> own ID-generation mechanisms. Asking for inconsistencies/problems.

This seems like a generic problem with every mechanism that uses IDs.

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Tuesday, 27 August 2013 21:55:46 UTC