Re: Executing script-inserted external scripts in insertion order

>> I fully expect LABjs to UA sniff past browsers. My point is that, 
ideally, for future browsers LABjs should be able to capability sniff so 
that a LABjs update isn't needed every time an additional browser implements 
the spec. And more to the point, a situation where a JS library used by many 
sites needs to be updated in order for an additional browser to implement 
the spec is a way to make it less likely that browsers implement the spec.

Yes, I agree with this as being the best option. I just want to guard 
against further complicating the (already complicated) issue by implementing 
a set of new attributes/features because the train has already left the 
station on the `async` feature (FF 3.6). It's a difficult situation to 
balance, but I'm kind of leaning toward it being the better of two evils to 
rely on the more simple approach (piggybacking on `async`) and dealing with 
the necessity of browser inferences for awhile longer. Yes, sites will need 
to keep their LABjs up to date, but that's far less impactful to overall 
page behavior than say asking them to keep jQuery up to date (with whom lots 
more potentially breaking changes go into each release). Since LABjs is so 
small and highly focused, I think the risk to sites keeping it up to date is 
minimized compared to jQuery.

Also, as far as I can see, all that LABjs needs to do is "sniff" for 
browsers that currently have `async` but who don't yet have the ordered 
behavior if its value is `false`. AFAIK, this is only FF3.6 (and FF4 
betas/nightlies), right? Maybe I'm wrong on that, but I think 
inferences/sniffing should be able to just identify these specific browsers 
and correct for that. I would hope that if we standardize the proposed 
`async=false` ordered behavior on injected scripts quickly, then any new 
browsers implementing `async` for the first time would do so fully and not 
partially. I know this is obviously quite optimistic, but it seems like the 
reasonable behavior to at least push the browsers for. "Don't implement 
`async` until you do it fully and correctly."

But even if some browser doesn't (which is quite possible), the only 
challenge then is for LABjs to update to sniff for that new browser and 
correct. Certainly not ideal, but I think it's possible and manageable (at 
least I hope so). I'm just thinking that's a better short-term fix (with 
short-term pain for longer-term bliss) than a more complicated feature set 
that noone has implemented yet.

But I can definitely see how the argument may go either way on it.

>> So if IE and WebKit stayed on the old script/cache code path even if they 
>> made script-inserted external scripts without the async attribute execute 
>> in insertion order, what carrot would you be giving IE and WebKit to 
>> motivate them to change (other than that it would be the Right Thing to 
>> implement the spec)?

I understand that IE/Webkit might need a carrot to agree to implement 
behavior they haven't implemented before. But I don't think that 
constructing a use-case (or engineering for it) where their old way loses 
out is the right way to entice them. I'm new to this standards body and to 
the process in general, but I feel like both Webkit and Microsoft (with IE9) 
are showing they clearly want to be in line with standards, so the high 
board approach of convincing based on merits of the standard is how I'd 
default to approaching the discussion. I *definitely* think it will take 
some community pressure and advocacy to make it happen.

And to be clear, obviously I'm not asking that the "as-soon-as-possible" 
script execution be removed... in some use cases, that's much better than 
the ordered execution. We're only discussing that the additional ordered 
behavior should be straightforwardly possible, and I'm further asserting 
that it's the more common use case than the non-ordered.

FWIW, there *is* a negative side-effect of IE/Webkit/Chrome forcing this use 
case to be solved with the hackish "script/cache" behavior. The side-effect 
is that the trick is completely ineffective (and in fact, quite detrimental) 
if the script being loaded has improper cache headers, because it results in 
a double-load (since the first preload didn't cache) *and* it also fails the 
assumption of immediate execution from cache as the second load must make 
the full asynchronous (non-ordered) round-trip again. So, in other words, 
the trick completely fails and breaks if proper cache headers aren't sent.

While we'd like to think that proper cache headers are a given, they 
certainly are not completely ubiquitous on the broader web. This is actually 
why LABjs only uses this trick as a last resort for preloading remote 
scripts (which can't be XHR'd like local scripts) -- because the assumption 
is there's more risk with the trick, but that if you're loading remotely, 
you're more likely to be loading from a CDN where (hopefully) caching 
headers are more likely to be correctly configured.

So, if anything, *this* could be the carrot (although indirect) to use with 
IE/Webkit/Chrome -- you are forcing a sub-optimal trick for this use case 
which not only complicates LABjs (and thus slows down its usage), but which 
also has non-trivial risk if used against scripts that aren't served with 
proper headers.

>> We could just as easily say that feature-test for `async` (and
>> specifically
>> its default value, either true or false) is the feature-test for
>> ordered vs.
>> non-ordered behavior.
>
> I hadn't considered this.

I guess I was specifically thinking that the default value being added to 
the feature-test might help distinguish it. I think currently in all `async` 
implementations, the default is `false`. But if we say that in 
order-preserving implementations, the default value is true for injected 
scripts (which fits your preference for defaults anyway), then the feature 
test can be not only for the presence of the `async` but also for its 
default value to be 'true', which would differentiate it from current 
`async` implementations.

>> Why would that be the performance-savvy default? It seems to me IE/WebKit 
>> behavior is more performance-savvy when there are at least two 
>> non-interacting scripts being downloaded in parallel (e.g. a site 
>> functionality script and an advertising script).

Here's my perspective on why I claim that ordered execution of parallel 
scripts is the performance-savvy way. I realize it's sort of 
counter-intuitive and I should have explained myself better before.

"facts" (observations):
1. There are far more sites on the web which currently use scripts with 
dependencies on each other (right or wrong) than on sites who load multiple 
scripts which have no dependencies.
2. In the majority of these sites, they are using manual script tags in 
their HTML markup, which as I've explained in another email comment to this 
thread (to Ian) has some significant performance disadvantages.
3. When those sites become aware of the potential savings that dynamic 
parallel loading will provide, they naturally are willing to make the 
change, as long as that change doesn't require significant refactoring of 
their own scripts, and especially of remote scripts they don't control.
4. As they make the switch to dynamic script loading (not using a smart 
loader like LABjs), they quickly realize that the beneficial behavior in 
FF/Opera of keeping the order makes their job a lot easier than the 
non-ordering behavior of IE/Webkit/Chrome.
5. As such, they then implement a script loader to try and help smooth out 
those difficult quirks. LABjs is small (so is RequireJS et al), but it's 
still less than optimal given that it has extra cruft in it to handle all 
these hacky tricks like "preloading".
6. Their conclusion (and mine)? For sites who need ordering (which are more 
common that those who care nothing about ordering), the ordered behavior 
(whether default or opt-in) is much more friendly to simple performance 
optimizations than is the non-ordered behavior (which significantly 
complicates things). For the browsers which provide no easy opt-in and 
instead force these ugly hacks for ordering, we are trading back some of the 
performance benefits (in the form of script loader complexity and also 
use-case risk side effects) we otherwise would have had if the standard 
ordered behavior from FF/Opera were *possible* everywhere (which would let 
LABjs be a lot simpler!)

>> Now you are talking about a different thing: Script-inserted inline 
>> script maintaining insertion order relative to script-inserted external 
>> scripts.

When I assert the previous behavior of FF/Opera as being desirable, I *only* 
mean the ordered part of injected *external* scripts. I have no care or 
feeling about the actual motivating behavior problem where the ordering had 
effects on injected inline scripts. I *only* am asserting behavior regarding 
injected external scripts.

>> I'm trying to find a solution that satisfies the constraints placed by 
>> existing jQuery and the constraints placed by existing LABjs. AFAICT, the 
>> jQuery constraint is about how script-inserted inline scripts execute 
>> relative to script-inserted external scripts but the LABjs constraint is 
>> about how script-inserted external scripts execute relative to each 
>> other. Thus, it seems to me that it's possible to satisfy both 
>> constraints at the same time. (By making script-inserted inline scripts 
>> not maintain order relative to script-inserted external scripts and 
>> making script-inserted external scripts with async=false maintain order 
>> among themselves.)

Yes I agree that the two behaviors are *not* mutually exclusive and both use 
cases can reasonably be supported. I think the original change you did 
(based on spec, no doubt) had the unintended side effect, which is why we're 
now discussing ways to separate the issue so that both can be served.

>> Maybe they aren't enjoying better performance in the case where a library 
>> like LABjs emulates in-order execution. However, they might be enjoying 
>> better performance in other (potentially more common) cases. I don't have 
>> measurement data, but on the face if things, it seems reasonable to 
>> assume IE and WebKit at least aren't putting themselves at a performance 
>> disadvantage when the execution order doesn't matter.

I explained above, but again... yes, there *is* a use-case for dynamic 
loading (with performance optimization in mind) where you don't care about 
order. And IE/Webkit/Safari serve that use-case well, and probably on 
purpose.

But this other use-case, where scripts are loaded that have dependencies, 
just seems pretty obvious to me as the more common use case. If you survey 
just the sites that use jQuery on the web, it's heavily common that those 
sites are loading jQuery separately from whatever page-code (or plugins) 
uses jQuery. Obviously, those sites aren't necessarily all wide-spread doing 
dynamic loading for performance (yet), but once they do, there will be 
millions of sites caught in this use-case we're discussing, and for *them*, 
IE/Webkit/Chrome will clearly be serving the non-majority use-case with only 
a barely workable hacky fallback for the more common use case.

This has to be (in my mind) the reasoning/"carrot" for how to convince 
IE/Webkit/Chrome to make a change to support ordered behavior, however we 
decide that should be opted into (or out of).

> I disagree. I think the old Gecko behavior had three characteristics that 
> deviated from the current spec draft, so the old Gecko behavior shouldn't 
> be considered as one either-or thing. I think two of those characteristics 
> were clearly undesirable and the third is potentially desirable.

As I said above, I completely agree with the assertion that injected 
external scripts should not have *any* other behavioral side-effects or 
dependencies on anything else (including injected inline scripts, 
parser-inserted scripts, etc)... EXCEPT each other (if necessary). I don't 
know the underlying code in the browser, but I know conceptually I consider 
injected external scripts to be a completely separate topic, and when I as 
an author inject them (using LABjs or otherwise), I want for them to be 
completely unpinned from anything else in the page (except each other).

> There's no chance that such a feature would be supported cross-origin 
> without CORS, because it would cause cross-origin information leaks that 
> aren't already present and unremovable for compatibility.

I actually didn't intend to suggest that external templating loading needed 
to support remote (CORS) type behavior. The cross-domain security issues are 
well documented, and none of what I've suggested for the templating use-case 
should imply that I was intending that behavior to work cross-domain. I just 
meant that loading a template from an external (local-domain) file helps 
performance by letting templates be independently cacheable. The 
cross-domain issue is an entirely different beast that I didn't mean to 
invoke.

>> I'm entirely unconvinced that maintaining execution order between 
>> script-inserted external scripts and parser-inserted scripts (so that the 
>> script-inserted script goes first and the parser-inserted script blocks) 
>> needs to be available at all.
>> I'm also doubting the necessity of having the option to make 
>> script-inserted inline scripts maintain order relative to script-inserted 
>> external scripts, even though I suggested it as part of my .ordered 
>> proposal, since if we had an explicit opt-in, providing this option would 
>> be easy. However, if we aren't doing .ordered, I'm not convinced we need 
>> to provide this option, since if a Web app wants to eval some text in the 
>> global scope after loading a set of external scripts, it can do so off 
>> the onload handler of the last one of those external scripts. The only 
>> case where value would be added is the case where the order is external, 
>> inline, external (all script-inserted that is). Seems a bit too special 
>> to me to support parallel download of the two externals here.

Addressed above. I am not arguing for injected external scripts to have any 
effect on, or dependency on, injected inline scripts OR parser-injected 
scripts of any kind.


--Kyle 

Received on Tuesday, 12 October 2010 15:58:27 UTC