[whatwg] Proposal for separating script downloads and execution

Steve Souders steve at souders.org
Tue May 24 16:12:47 PDT 2011


In many (all?) cases below the term "execution" is meant to include 
parsing and compilation. I know that's what Nicholas and Kyle have in 
mind, and is the motivation behind Gmail's comment hack and my ControlJS 
library.

If browsers processed (parsed & compiled) scripts in a background thread 
it would mitigate the problem, but not solve it. Suppose I have 100K of 
JS I need right now to generate the DOM for the initial page, and I have 
another 500K of JS that's only needed if the user clicks on FeatureX. 
Assuming there's only one background thread, I want to prioritize the 
first 100K of JS on that thread, and not have it blocked by the 
unnecessary processing of the second script. Also, I only want to do the 
processing on the second script if the user activates the feature. This 
is important on mobile now to reduce power consumption but is also 
important on desktops as CPUs become more power sensitive and JS 
payloads grow.

-Steve


On 5/23/11 6:35 PM, Ian Hickson wrote:
> This thread was rather light on use cases and heavy on conjecture and
> proposals. To make sure that we don't add features that are not actually
> necessary, I have reordered this thread to go through the use cases first,
> and to then only consider proposals based on how they address those use
> cases. I have ignored all arguments drawn from conjecture and only looked
> at those based on measured data or first-hand knowledge. If you think I
> have ignored your e-mail incorrectly, please do not hesitate to bring my
> attention to it; despite reading through the 158 messages on this thread
> twice, I may have missed something important! Finally, at the end of this
> e-mail I respond to several e-mails that purport to describe problems but
> fail to do so, to illustrate the importance of actually describing the
> real problem we are trying to solve before suggesting solutions.
>
>
> USE CASES
>
> Problem A:
>
> On Tue, 8 Feb 2011, John Tamplin wrote:
>> simply parsing the downloaded script takes a lot of time and interferes
>> with user interaction with the UI, so as awkward as it seems,
>> downloading the script in a comment in the background, and then
>> evaluating it when needed does provide a better user experience on
>> mobile devices.
>>
>> See
>> http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html
>> for the official blog post about this technique.
> The problem here seems to boil down to "we want our script-heavy page to
> load fast without blocking UI, but browsers block the UI thread while
> parsing after downloading but before executing".
>
>
> On Thu, 10 Feb 2011, John Tamplin wrote:
>> [...] on mobile phones in particular the process of reading the contents
>> of a script tag [...] is way too slow and interrupts the UI.
> Right, that's the same problem.
>
> I shall refer to this as "problem A" below.
>
>
> On Fri, 11 Feb 2011, Glenn Maynard wrote:
>> Javascript applications are bigger and more complex than they used to
>> be, and they'll only get bigger and yet more complex.  Having codebases
>> several megabytes in size in the future seems a fair prediction.  Once
>> we get to that point, having browsers parse all of that at once, no
>> matter how fast parsers are, seems unreasonable
> Why? The parsing doesn't have to block loading; it can all happen in the
> background, while the page is loading.
>
>
>> we should have a solid framework to allow modular codebases, as every
>> other serious application platform has.
> I'm not sure what you mean by "modular". Do you mean like dynamic linking
> to libraries, where code is loaded later? We can do that now, just insert
> a<script>  element dynamically. Do you mean like system services? We can
> do that now, with shared workers, which load in the background and are
> shared amongst tabs. Do you mean like namespacing? That's a language-level
> feature, I don't think HTML would be the right place to solve that.
>
> I'm not sure what problem you mean here exactly.
>
>
>> It also seems like it may become very useful to allow browsers to spend
>> time (whether idle time or otherwise) not just on parsing but on more
>> expensive optimizations, and having a framework that gives them access
>> to scripts to do that in advance seems like a very good idea.
> <script async>  seems to do this already.
>
>
> PROPOSALS
>
> The simplest solution to problem A seems to be to have the browsers do the
> script parsing on a background thread, rather than blocking the UI. This
> requires no changes to the specification at all. It can be combined with
> lazy downloading by inserting a<script>  node when the script is needed;
> basically, it is combining the "downloading" and "parsing" background
> steps into one. There doesn't seem to be any need to treat them as
> separate steps for solving problem A.
>
>
> On Tue, 1 Feb 2011, Nicholas Zakas wrote:
>> Add a new attribute to the<script>  called noexecute (for lack of a
>> better term) that instructs the browser to download the script but do
>> not execute it. Developers must manually execute the code by calling an
>> execute() method on the script node. Simple example:
>>
>> var script = document.createElement("script");
>> script.noexecute = true;
>> script.src = "foo.js";
>> document.head.appendChild(script);
>>
>> //later
>> script.execute();
> Given that script execution (as opposed to the preprocessing that occurs
> before execution, including parsing and compilation) can be trivially
> fast (e.g. by making the script do nothing but expose an object), what is
> the benefit of delaying the execution?
>
> This doesn't seem to address problem A.
>
>
> On Tue, 1 Feb 2011, Kyle Simpson wrote:
>> In step 12:
>> "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."
>>
>> In other words, you can begin downloading one or more scripts (but not
>> executing them) by simply creating a script element dynamically and
>> setting its `src` property. The script will not be executed (even if it
>> finishes downloading) until the script element is added to the DOM. In
>> this way, you can easily create several script elements (but not append
>> them to the DOM), and then when you want to execute them, you simply
>> append them to the DOM in the order you prefer.
> Given that the time the script takes to execute is already under the
> control of the author, and can be trivially short, this solution doesn't
> seem to address problem A: anything the browser does in the background
> before the<script>  is inserted can just as easily be done in the
> background after the<script>  is inserted.
>
>
>> IE goes one step further, which I think is useful, which is to give a
>> `readyState` (and `onreadystatechange` event handling) to the script
>> element, which notifies the code of the state of this "preloading". Why
>> this is useful is that you may choose to wait until all scripts have
>> finished loading before starting to execute them. Being notified of when
>> they finish loading (but not executing) can be a very useful addition to
>> this technique.
> The problem with this technique is that there's no guarantee that the
> script will be downloaded at all -- it depends on the UA's belief about
> what will lead to the best experience. For example, if the system has idle
> cycles, it might happen sooner than if the UA is extremely busy already.
> That's rather the point of the feature. :-)
>
> Also, readyState isn't actually especially useful here, at least not in
> the context of problem A. Consider the two possibilities: (1) by the time
> you want to run the script, it is already loaded, and (2) by the time you
> want to run the script, it is not yet loaded. In (1), you can insert the
> element into the DOM and it'll just work. And in (2)... well you want the
> script to run ASAP, so why wait? You just insert the element into the DOM
> and as soon as it can, the UA will execute it, and so again, it just
> works. No need to track when it is ready.
>
> If you need to track when it's ready to make sure you execute another
> script after it, then just using the 'load' event on<script>  is
> sufficent: just wait for the previous script to have run, then insert the
> one you care about. But really, it seems better to structure your scripts
> so that they don't do anything when they are run except expose a callback
> that you can run when you want the code to run.
>
>
>> The wording in the spec lists this idea as "may". I suggest that the
>> discussion Nicholas has proposed should shift to discussing if the spec
>> should:
>>
>> 1) change "may" to "shall" or "will" to move it from being a suggestion
>> to being a directly specified thing (that way the other browsers besides
>> IE have incentive to eventually include it)
> (As a side-note, in the HTML spec the words "shall" and "will" don't have
> any normative meaning. See the "Conformance requirements" section.)
>
> We could change "may" to "must", but it would merely constrain
> implementations further: instead of being able to optimise in certain
> situations by _not_ fetching scripts that might never be used, it would
> force the network to be used in these situations. That seems like a loss,
> and does nothing to address problem A.
>
>
> On Tue, 1 Feb 2011, Nicholas Zakas wrote:
>> The major issue I have with the way the spec is written is that there is
>> no way to feature detect this capability.
> That is entirely intentional: it's a UA optimisation; we don't want to
> expose those, as it constrains what UAs can do to improve.
>
>
> On Thu, 3 Feb 2011, Jonas Sicking wrote:
>> One reason I like the noexecute proposal more than relying on readyState
>> is that noexecute can be used in markup. I.e. you can do things like:
>>
>> <html>
>> <head>
>> <script src=a.js noexecute onload="...">
>> <script src=b.js noexecute onload="...">
>> <script src=c.js noexecute onload="...">
>> </head>
> What would the onload="..."s be? I don't understand the benefit of not
> executing the scripts here. If you want your scripts to not do anything,
> just have them not do anything except expose a function that you can call
> whenever you want the actual code to run.
>
>
> On Thu, 3 Feb 2011, Kyle Simpson wrote:
>> Doesn't<link rel=prefetch>  mostly address the use-case of
>> load-but-don't-execute in markup?
> <link rel=prefetch>  doesn't solve problem A because it doesn't give the UA
> any hint that the resource is a script it should compile.
>
>
> On Thu, 10 Feb 2011, John Tamplin wrote:
>> [problem A] has driven crude hacks like the comment hack, which in fact
>> precludes the browsers [ever] getting smarter about doing the
>> parsing/etc in the background or during idle time.
> I don't see why it would preclude them from getting smarter. The smarts
> wouldn't improve the pages with the hacks, but that's ok. It doesn't hurt
> them either.
>
>
>> This proposal is about a way to hint to the browser that only the
>> download part should happen now, and the parsing/execution of the
>> downloaded script will happen later, which in fact enables smarter
>> browsers to make smarter decisions.
> That doesn't solve problem A: you still end up blocking the UI when you do
> the parsing if that's all you change.
>
>
> On Fri, 11 Feb 2011, Bjoern Hoehrmann wrote:
>> [...] some analyis beyond the simple "<script>  processing seems slow
>> right now, so let authors say don't process a<script>  with side-effects
>> until the author commands it" is. In Perl for instance the problem is
>> solved with AUTOLOAD, a routine that is called when a subroutine is
>> undefined, to act in its stead.
> AUTOLOAD doesn't reduce the time the parser spends blocking the UI thread;
> it just spreads the time around. That doesn't really work for UI, where
> it would cause jerkiness.
>
>
> On Wed, 9 Feb 2011, Alexandre Morgaut wrote:
>> <link rel="prefetch" type="text/javascript" src="myscript.js">
>>
>> let the link HTML Element have an execute() method [...]
> This seems equivalent to the noexecute="" idea, but with different (worse)
> legacy behaviour. I don't see how it solves problem A.
>
>
> On Wed, 9 Feb 2011, Alexandre Morgaut wrote:
>> Adding a "required" relationShip could be more appropriate way
>>
>> <link id="someScript" rel="required" type="text/javascript" src="someData.js">
>> <link id="someData" rel="required" type="application/json" src="someData.json">
>> <link id="aTemplate" rel="required" type="text/html" src="myTemplate.html">
> This seems the same.
>
>
>> This would still need:
>> - a "content" property on HTML link elements (which may be also useful to access raw CSS definition)
> This wouldn't work for cross-site cases.
>
>
> On Wed, 9 Feb 2011, John Tamplin wrote:
>> Well, there is only a certain amount of processing power to go around.
>> No matter how well it is implemented, time spent parsing is time that
>> can't be spent doing other things if the app is pushing the client to
>> the limit, and it makes sense to let the app provide hints of when is a
>> good time to spend that effort and when isn't a good time.
> It seems like "when there's nothing else going on" is the best time. How
> would the script know when that is better than the UA?
>
>
>> I am not sure I understand why you are so opposed to providing a
>> mechanism for an application to tell the browser it would like the
>> parsing to not necessarily be performed immediately on a downloaded
>> script.
> It's clear that the parsing/compiling has to happen between download and
> execution, it seems that the browser is in the best position to know when
> it could do it with minimal impact on the rest of the system. If you just
> want it to happen soon but don't care exactly when, we have "async". If
> you want it to happen now, then the spec also supports that. It's not
> clear why anything else is needed.
>
>
> On Tue, 22 Feb 2011, Glenn Maynard wrote:
>> 3. My (and Nicholas's previous) proposal: Script elements are fetched
>> when inserted into the DOM[1].  An event is dispatched when the script
>> has been fetched, eg. "onfetch" or "onpreload"[2].  A "preload"
>> attribute is added; when true, the script will not be executed when the
>> fetch completes; call script.execute() to run the script.
> The "preload"/onpreload part of this seems unnecesssary to solve problem
> A: by the time the event fires, all the difficult work is done, and the
> execution (the only thing this would allow you to delay, and the only
> thing that has to block the UI thread) can be trivial in comparison. It
> would be equivalent to just wrapping the code in the script in a function
> that invokes the event manually and lets you call a callback.
>
>
> The only proposed solution that seems to actually solve problem A is for
> the browsers to do the compilation stage in the background. There's
> nothing we need to add to the platform to support this, it will just work.
>
> Now it's possible that there are other use cases that we should consider
> that were not raised on this thread. If so, I encourage people to explain
> the problem / use case with concrete examples in a new thread, without
> conflating them with proposals. Then we can consider how to solve them.
>
>
>
> SOME OTHER COMMENTS
>
> On Tue, 1 Feb 2011, Nicholas Zakas wrote:
>> Problem Statement:
>>
>> Loading JavaScript onto a page poses several performance issues.
> Which issues?
>
>
>> With a regular<script>  tag, the UA waits for download and then waits
>> for execution. The defer attribute helps by not blocking on download and
>> deferring execution until later but preserves execution order; the async
>> attribute helps by not blocking on download but does block on execution
>> (the timing of which cannot be controlled) and does not preserve order.
> This doesn't seem to be a problem.
>
>
>> Each of the existing solutions shifts around when download and execution
>> happens by giving developers control over when the download occurs but
>> only minimally when execution happens. As a result, developers have
>> created ever more ingenius/fragile solutions to allow the separation of
>> script downloads and execution. Examples:
>>
>> 1. Preloading JS without execution
>> (http://www.phpied.com/preload-cssjavascript-without-execution/) by
>> Stoyan Stefanov, which describes how to download JavaScript without
>> execution it, as a cache-warming technique.
>>
>> 2. ControlJS (http://stevesouders.com/controljs/) by Steve Souders,
>> which extends Stoyan's model to allow on-demand execution of scripts.
>>
>> 3. Gmail putting JavaScript in comments and then parsing later
>> (http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html)
>> to enable download without execution and then execution on-demand.
> What problems do these solutions solve?
>
>
> On Thu, 3 Feb 2011, Steve Souders wrote:
>> This behavior is important to the next generation of web apps,
>> especially for mobile.
>>      - download the bytes of JS now
>>      - execute later when specified (eg, when the user initiates a feature)
> Why?
>
>
> On Tue, 8 Feb 2011, Kyle Simpson wrote:
>> For the purposes of this discussion, we are combining (but safely so, I
>> believe) "execution" and "parsing", and saying that we want to be able
>> to defer the "parse/execution" phase of script loading.
> I don't see any good reason to combine them. They seem emminently
> separable and have very different characteristics (e.g. one has to be
> synchronous, the other does not; one has side-effects, the other does not,
> one's results can be cached, the other cannot, one can be made essentially
> zero-cost by the author, the other cannot, etc).
>
>
> On Tue, 8 Feb 2011, John Tamplin wrote:
>> I think it is useful to provide ignorable hints to browser to indicate
>> that you want this downloaded but you don't need it parsed right away.
> What's the use case?
>
>
> On Tue, 8 Feb 2011, Kyle Simpson wrote:
>> I would first refer you to the use-cases that Steve Souders has
>> documented around his ControlJS library. His commentary on this topic is
>> far more comprehensive than anything I can rabble off here.
>>
>> http://www.stevesouders.com/blog/2010/12/15/controljs-part-1/
> I don't really see any use cases there.
>
>
> On Fri, 11 Feb 2011, Will Alexander wrote:
>>    Consider the controljs example in which the menu code does not load
>> until it is clicked.  There's no requirement that it run synchronously
>> so it is acceptable for the script's execution to simply be scheduled in
>> response to the click event.  A non-prefetching browser would not be as
>> "performant" but would still work.
> Why not just run the code sometime before it is needed, while the page is
> idle? Why is it necessary to delay the load until the last possible
> minute? What problem are we solving? Problem A can't be the problem being
> solved here, since the execution takes a trivially short time compared to
> the download and compiling.
>
>
> On Tue, 8 Feb 2011, Kyle Simpson wrote:
>> No matter how good the implementation of the JavaScript engine on
>> mobile, the mobile device will always be much more limited in processing
>> power than a desktop browser environment.
> Mobile devices are hardly limited in power anymore. Heck, by the time
> anything we discuss here is widely implemented, the average phone will
> probably be dual-core. Our phones these days are ridiculously powerful.
>
>
>> 1. One use-case that I *am* quite familiar with is: script loaders (like
>> mine, LABjs) have the need to be able to download multiple scripts in
>> parallel (again, for performance optimization, but not just for
>> mobile!), but it's quite common that some scripts have dependencies on
>> each other.
> Script loaders aren't a problem, they're a solution. What is the use case?
>
>
>> The problem is that scripts loaded dynamically are not guaranteed to
>> execute in any particular order. A mechanism for loading files in
>> parallel but controlling (or enforcing) their execution order, is
>> necessary.
> There are a number of solutions to this problem now (onload, defer/async,
> .async, the careful definitions of insertion/execution order, etc). What
> is wrong with them that we need more solutions?
>
>
>> For instance, if I have two groups of scripts ("A.js", "B.js" and
>> "C.js") and ("D.js", "E.js", and "F.js"). Within each group, the order
>> must be maintained, but the two groups are completely independent. As
>> "async=false" is currently implemented, you cannot accomplish isolating
>> the two groups of scripts from affecting each other. The "D,E,F" group
>> will be forced to wait for the "A,B,C" group to finish executing.
> When does this happen? Do you have a concrete example?
>
> Note that there are multiple solutions to this already:
>
>   - put A,B,C into one file and D,E,F into one file.
>   - write the scripts so that they don't rely on each other during
>     execution, but instead expose a function that you can call when you
>     want, e.g. when they are all loaded.
>   - run the scripts in two iframes.
>   - create<script>  elements ahead of time and insert then in order,
>     allowing the browser to download and compile them in parallel, but
>     insert them in the order you need them, when they are ready, using
>     onload to trigger B from A and C from B, etc.
>
>
>> 2. Another plausible use-case that occurs to me is loading two
>> overlapping plugins (like for jQuery, for instance). The author may have
>> a simple calendar widget and a much more complex calendar widget, and
>> the two may conflict or overlap in such a way that only one should be
>> executed. But for speed of response, the author may want to "preload"
>> both plugins and have them waiting on hand, and depending on what action
>> the user takes (or the state of data from an Ajax request), may then
>> decide at run-time which of the two plugins to execute.
> Do you have a page that tries to do that kind of thing? I don't think I've
> ever come across this kind of thing.
>
>
>> In 95% of cases, I'd probably agree.
> We're only generally aiming for 80% of cases, so if what we have now
> handles 95% of cases, we're well past done!
>
>
>> Most of the time, when a dev is painted into the corner in one of those
>> 5% of cases where the browser's "smarts" are hindering their goal, it
>> feels like the balance is too far to the side of "let that be black-box
>> inside the browser and you just don't worry about it" to give a web
>> author much of an option.
> We're explicitly not trying to handle rare cases. There's too many big
> things to fix still to worry about the little ones.
>
>
> On Wed, 23 Feb 2011, Boris Zbarsky wrote:
>> On 2/23/11 9:14 AM, Kyle Simpson wrote:
>>> I strongly disagree with this characterization, based solely on the
>>> fact that the wording of the current spec already says to do exactly
>>> as I'm proposing. That's not a "compatibility hack", that's further
>>> standardizing the wisdom that the spec writers already thought through
>>> and codified.
>> I think you're overestimating the "wisdom" and "thought through", but
>> I'll let hixie respond to that.  It's just as likely that he just
>> specced the IE behavior as optional because IE was already doing it and
>> wasn't planning to change, and moved on.
> Boris is entirely correct.
>
>
> On Thu, 3 Feb 2011, Kyle Simpson wrote:
>> However, it's already possible to address that same use-case using
>> existing behavior... by simple specifying a bogus `type` for the inline
>> script element. Some JS templating solutions make use of this behavior,
>> like:
>>
>> <script type="template/foobar">
>> ...
>> </script>
> Note that per spec this can't be a _bogus_ type, it has to be the actual
> type of the data used in the data block.
>
>
> On Tue, 22 Feb 2011, Glenn Maynard wrote:
>> To briefly restate the third proposal, so it's not lost in the noise [...]
> Don't worry, I read every e-mail.
>
>
> Incidentally, just as a parting note: a lot of e-mails on this thread
> seemed concerned about how hard something was to spec, for example
> preferring solutions grounded in existing spec text, and avoiding
> solutions that might be simple to implement but hard to spec. For the
> record, how hard something is to spec is not an important consideration.
> What matters is whether we solve use cases for authors, and within those
> constraints, that we get something interoperable implemented in all the
> relevant user agents (typically browsers, validators, or editors).
>


More information about the whatwg mailing list