[whatwg] Script preloading

Bruno Racineux bruno at hexanet.net
Wed Oct 2 03:22:40 PDT 2013



On 8/27/13 2:55 PM, "Ian Hickson" <ian at hixie.ch> wrote:

>
>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.

I agree on the verbosity with the double markup. It feels redundant.
But perhaps so is the repeat of the script name with 'needs="A.js"' for
every script that would need a dependency.

>
>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.

Well fair enough from a purely hand written static page sense.

Note that the w3c proposal has now evolved with 2 new attributes:
lazyload+postpone
https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourcePriorities/Overvi
ew.html#lazyload-attribute

I think a 'preload' addition could very well answer the 'when-needed' part.
I'll try to put an example below.

Either way, it's worth keeping in mind that those two specs have to
seemingly work together without conflict between the two.

>
>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. :-)

Well browsers now do it, but on the different basis. My approach was to
compile entire scripts vs functions bodies as it's apparently currently
done.

>
>> 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.

Nods. But if recompiling or looking up script+chars pairs are much slower
than deserializing a compiled representation of an entire scripts,
it might be worth considering down the road...

>
>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.h
>tml
>
>
>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).

Right.

>
>> 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?

Well for one, many inline functions have no need to run before
'interactive' fires. A delay or 'promise' at this stage is too early and
rarely makes sense. Currently most common domReady hooks rely on
DOMContentLoaded.
As such I see no very good reason to run such plugin code prior to
DOMContentLoaded.

e.g I want to defer jQuery and I want my small inline script events (based
on the context of a page) parsed and run post jQuery, right after
DOMContentLoaded fires.
That priority level is something that cannot be done right now.


>
>> 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?

Because for inline script, it implies registering onReady promises early
(knowing fairly well that jQuery is not ready) and having javascript do
more than it should.

Aside from the IE6-9 Defer bug, which made 'ordered defer' somewhat
useless until IE10, 'ordered defer' is a useful flexibility for the future.


>
>> 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.

I agree, I just find the proposed solution slightly convoluted.
And I still think that some of this stuff is best handled via js objects
and functions.

The main part I would want the DOM to do is to hold 'execute' and give me
a very simple way to run the script at will, which link[rel=subresource]
doesn't do or address too well either.

Also something that makes me cringe, is the verbose repeat of long paths
(just like srcset does)

If you need dependencies, then how about something shorter like a
dependency 
Index, with a 'preload' semantic on top of the lazyload+postpone proposal,
like:

<script src="jquery.js" preload branch="A1">
<script src="plugin1.js" preload branch="A2">

<script src="plugin2.js" preload branch="A2">

<script>
	E('plugin2.js');
</script>

i.e. The browser would takes the letter as indicator of a dependency tree
branch,
and follow along it's priority number to determine the dependencies to
load when-needed.
Or perhaps using the id attribute with the letter+number syntax form.


>
>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?

My worry with this, is a performance caveat and potential increasing bloat
in pages,
vs properly isolating what's needed on a per page basis. Platform like
wordpress or drupal, or poorly written plugins in any platform, already
too often add scripts globally to all pages, making pages an increasing
piece of FUBAR script bloat.
With the 'whenneeded' concept, it can become quite tempting to put many
many scripts in the DOM without any guilt, or any sensible notion of
wether it's actually needed or not for a particular page.

>
>> 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-librar
>>ies-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.   `._.-(,_..'--(,_..'`-.;.'




More information about the whatwg mailing list