Re: Executing script-inserted external scripts in insertion order

Adam Barth wrote:
> On Thu, Oct 14, 2010 at 12:48 AM, Henri Sivonen <hsivonen@iki.fi>
> wrote:
> >> >> Yeah, I don't know. I don't mean to be negative, it just seems
> >> >> like a
> >> >> really obscure feature.
> >
> > I think it's more obscure to make async have no effect on
> > script-inserted external scripts while it has an effect on
> > parser-inserted external scripts than to make it have the same
> > effect on both. (Well, not exactly the same, since non-async
> > parser-inserted external scripts maintain order relative to
> > parser-inserted inline scripts, but the ideas being discussed for
> > script-inserted scripts don't involve non-async script-inserted
> > external scripts blocking script-inserted inline scripts.)
> 
> I meant obscure as in not many people would discover and use the
> feature.

I'd expect people who'd use async in markup as appropriate to use async in script as appropriate. The way to become aware of async is probably to read some perf-oriented blogs or books.

> Even explaining what the feature does is very complicated.

async (when extended so that it has an effect on both script-inserted and parser-inserted scripts and when the default for both is ordered execution) has a one-sentence elevator pitch:

"Async makes external scripts execute as soon as they've been downloaded without blocking other scripts form executing while the async script is downloading in contrast to the normal behavior of waiting until earlier external scripts have been downloaded and executed and blocking subsequent scripts from executing."

OK, it's a bit long for one sentence.

> Do async external scripts really behave different than script-inserted
> external scripts? My understanding is that they both execute as soon
> as the bytes are available from the network (at least in the spec
> and/or WebKit).

In the spec and in mozilla-central right now and (I believe but didn't test) in WebKit trunk, parser-inserted external scripts with the async attribute set behave exactly like script-inserted external scripts regardless of the state of the async property on those script-inserted external scripts.

I consider it rather surprising that this is the case--that is, I think its surprising and counter-intuitive that the state of the async property currently has no effect on script-inserted external scripts when it does have an effect on parser-inserted external scripts.

Note that in Firefox 3.6, the state of the async property does have an effect on both script-inserted external scripts and on parser-inserted external scripts. In both cases, async=false is the default and makes the external scripts maintain order and async=true opts into the executing as soon as downloaded without blocking subsequent scripts.

> > I agree that making script-inserted external script maintain order
> > among themselves by default and making async=true opt into unordered
> > as-soon-as-possible execution would be a virtuous solution on almost
> > all counts:
> >  * The safe behavior would be the default for authors who aren't
> >  prepared to handle the less safe behavior (once IE and WebKit
> >  versions that have the old unsafe default have faded away)
> >  * It would allow all browser to Support Existing Content (though IE
> >  and WebKit would also have to continue to support script/cache
> >  preloading to Support Existing Content)
> >  * The default would go the same way (async=false) for both
> >  script-created and parser-created scripts.
> >
> > The big question is if we can get to interop via this route. That
> > depends on whether IE and WebKit are willing to change their default
> > behavior for script-inserted external non-async scripts. Are they?
> 
> It's difficult for me to foresee the consequences of doing that. One
> of the nice things about the other aspects of the parser is that we
> can grep the web to get a good sense of the order of magnitude or
> pages that are affected by each decision.
> 
> In your proposal, would the async parser-inserted scripts maintain
> order with the script-inserted scripts?

No. I'm suggesting that async=true scripts behave just like async=true scripts per spec now.

The change I'm suggesting is this: async=false script-inserted external scripts would go to a new queue that doesn't now exist in the spec. This would be (2) in your list below. When a script in that queue finishes being fetched, the first script from the start of the queue would be executed and removed from the queue repeating until the first script in the queue hasn't finished being fetched yet or the queue is empty.

As a consequence of async defaulting to false, this would mean that the behavior described in the paragraph above would become the default behavior for script-inserted external scripts whose async property hasn't been changed explicitly to true. Thus the default behavior for script-inserted external scripts whose async property hasn't explicitly been set to true differ from the current default in IE and WebKit.

> We'd essentially have four
> kinds of external scripts:
> 
> 1) Parser-blocking
> 2) Asynchronous but ordered among this group
> 3) Deferred to after parsing and ordered among this group

Where "this group" is a different "this group" than in case (2).

> 4) Asynchronous-as-soon-as-possible

Yes, we'd have four kinds of external scripts.

> The unadorned external scripts would be (1). 

Only if parser-inserted. As with the spec now, there'd be no way for a script-inserted script to block the parser. (In Firefox 3.6 there was; I think we shouldn't go back to having that possibility.)

> Both async and
> script-inserted would be (2).

No, I'm suggesting that script-inserted external scripts with async=false would be (2) and that async=false be the default state.

> Defer would put the script into (3).

Only parser-inserted though. As currently specced, there'd be no way for a script-inserted script to go into the defer group, and I'm not suggesting changing that.

> If you where both external and marked async, that would put you in
> (4).

Yes.

> Is there an ordering dependence between (2) and (3)?

Only when both groups happen to be ready to execute something. Group (2) can gain new members before or after (3) has been emptied. Furthermore, scripts in either group that have not yet finished fetching don't inhibit scripts in the other group from executing.

In the patch I've written for Gecko, if it is time to execute scripts from group (3) and the first script in group (2) has finished fetching, group (2) goes first (until the group is empty or the first script in the group hasn't finished fetching yet), then group (3) is executed (until the group is empty or the first script in the group hasn't finished fetching yet). This happens without yielding to the event loop, though I'd be OK with the spec permitting event loop spins between any two scripts in this scenario.

I don't care much about which one goes first when both are ready: (2) or (3), but it seemed more natural to make (2) go first. It doesn't really matter in the case where fetching takes non-zero time, so you could only black-box test it with data: URLs anyway. That is, even when (2) gets priority over (3) when both are ready, you could always explain a client-only black-box observation of (3) going first by assuming that the first script in (2) hadn't finished being fetched yet. 

> That would seem
> to be worthwhile in this plan in the spirit of predictability. It's
> unfortunate that you have to use JavaScript to access the fastest
> group, which is (4), but we might hope that we're saving web authors
> from themselves because (4) is the hardest to author correctly.

You can access group (4) also by specifying the async attribute in markup.

> How does the "ordered" property fit into this picture? I'm not sure
> if you've dropped it from your proposal or whether I'm not not clear
> how it fits in.

It's dropped.

-- 
Henri Sivonen
hsivonen@iki.fi
http://hsivonen.iki.fi/

Received on Thursday, 14 October 2010 09:15:19 UTC