[whatwg] Proposal for separating script downloads and execution

Nicholas Zakas nzakas at yahoo-inc.com
Tue May 24 09:34:45 PDT 2011


Hi Ian,

Wow, thanks for the excellent summary and discussion points. Since I helped instigate this thread, I'll try to help clarify some of my points. My point of reference is as the front end tech lead for the Yahoo! homepage (www.yahoo.com) redesign, as I'm talking about issues below, they are related to that experience.

There is a general need on the web for speed (duh). We want not just the current experience to be fast but the *next* experience to be fast. The next experience is frequently made up of HTML, CSS, and JavaScript that isn't needed immediately, and so is deferred. The timing of when to download these resources is something that we've struggled with due to the various ways different resources get loaded.

We did a number of things on the Yahoo! homepage to help the next experience be fast (note: not all are still deployed):

1) After page load, we started to load CSS and JavaScript for the apps we previously had on the left side of the page. The intent was to make sure they came up as quickly as possible.

2) Preload images that may be needed for the next experience via (new Image().src = "foo.png").

3) We delayed loading JavaScript for the search assist dropdown until the user set focus to the searchbox.

Your assertion that loading a file that simply defines a function will solve the problem is a bit too simplistic for most web applications. This is what we did in #1 and it actually caused performance problems as the browser would stop to execute the script as it came in, interrupting whatever the user was already doing. Amazingly, delays of longer than 100ms are actually perceivable by users[1], and small delays interrupt running animations very noticeably (a common complaint we had while testing some of the preload strategies).

Moving parsing and compilation to a background thread solves part of the problem, the problem where doing so freezes the UI currently. It doesn't solve what I consider to be the important part of the problem, and that is the inability to have a JavaScript resource downloaded but not applied to the page. The best next experience can only be achieved when the resources are ready and then applied at the right moment.

To be clear: this is not a problem that is unique to JavaScript. The same problem exists with all external resources, such as images and CSS. The difference is that both images and CSS allow you to download the resource and then apply to the page later when you want (images via new Image(), CSS via dynamic <link> element that is only applied when added to the page).

My request to have this for JavaScript, and the subsequent proposal on how to do it, was really to get parity with the other external resources so that we can get all of the resources for the *next* experience ready and available for application only when appropriate rather than trying to do a dance to get the timing right.

As discussed in the thread, there are pockets of people who are already trying to solve this problem using existing technology, and are only getting part of the way there using hacks. I put this in the category of CSS selectors in JavaScript: the use case wasn't very obvious, but clearly there was a problem people were trying to solve and this became a common solution that was later standardized. I think we're in that area for this JS downloading/executing issue as well.

- Nicholas

[1] http://www.useit.com/papers/responsetime.html

-----Original Message-----
From: Ian Hickson [mailto:ian at hixie.ch]
Sent: Monday, May 23, 2011 6:36 PM
To: whatwg at whatwg.org
Subject: [whatwg] Proposal for separating script downloads and execution


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


More information about the whatwg mailing list