W3C home > Mailing lists > Public > whatwg@whatwg.org > May 2011

[whatwg] Proposal for separating script downloads and execution

From: Ian Hickson <ian@hixie.ch>
Date: Tue, 24 May 2011 01:35:55 +0000 (UTC)
Message-ID: <Pine.LNX.4.64.1105232146420.19153@ps20323.dreamhostps.com>

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

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'
Received on Monday, 23 May 2011 18:35:55 UTC

This archive was generated by hypermail 2.4.0 : Wednesday, 22 January 2020 16:59:33 UTC