- From: Ben Maurer <ben.maurer@gmail.com>
- Date: Sat, 23 Aug 2014 10:12:26 -0700
- To: Ian Hickson <ian@hixie.ch>
- Cc: "whatwg@whatwg.org" <whatwg@whatwg.org>
Thanks for writing this all up. A few questions/thoughts: (1) Dependencies in this model seem to be strict execution dependencies. It's possible some use cases might want to use dependencies to describe loading priority. As an example, imagine a Facebook page with 3 JS files: Feed.js -- Needed to render the user's feed Chat.js -- Displays the user's friends who are online PhotoViewer.js -- When the user clicks a photo, this file creates a photo viewer allowing the user to browse an album. Until the user clicks something, this file isn't needed. Roughly, the behavior we want here is: load Feed, then Chat, then PhotoViewer. But if the user clicks a photo move PhotoViewer to the front of the line. How do I express the relative ordering of Feed vs Chat vs PhotoViewer? I wouldn't want to use dependencies, because if PhotoViewer depended on Chat then if I call .load() on PhotoViewer it will force Chat to be downloaded. (2) Can inline scripts have needs? This seems like something potentially useful (eg, you include a script tag for Google maps then you have an inline script to create a map. You want the inline script to only execute once maps is loaded) (3) How does loading interact with the cache? For example, if I have a <link load-policy="declare"> and the resource is in the browser's cache, would the ready promise be fulfilled? This could be very useful for applications -- sometimes there is functionality you are willing to execute immediately if the resource is in the user's cache. On Fri, Aug 22, 2014 at 5:44 PM, Ian Hickson <ian@hixie.ch> wrote: > > When I last looked at preloading scripts, it was pointed out to me that > the Web Perf WG had some work ongoing in this space, so I decided to let > them take care of it. However, nothing much seems to be happening there, > and so I'm looking at this again. It's also been pointed out that limiting > this to just scripts preloading maybe taking too narrow a view, and that > we should also consider preloading and deferring loading of other > resources, such as images, style sheets, and so forth. > > I will start by looking at use cases again, then go through some of the > feedback on the last proposal I'd made in this space, and then make a new > proposal intended to address the use cases and feedback. > > On Fri, 15 Aug 2014, Ben Maurer wrote: > > > > [Use-case F:] A website has a page where media is the primary content. > > It would like to make sure that media is downloaded before JS [e.g. > > flickr, youtube, facebook photos] > > > > [Use-case G:] A website knows there's a piece of Javascript code that > > the user might need if they click on a part of the page. The developer > > would like to have the user download it, but not at the expense of other > > resources. > > > > [Use-case H:] A website is prefetching photos in a photo album and would > > like to make sure these images are lower priority than images the user > > is actually viewing. > > On Mon, 28 Jul 2014, Ben Maurer wrote: > > > > [Use-case I:] Facebook uses an automated pipeline to decide what CSS/JS > > files to package together. If we could pass a custom header in the > > request for a CSS file (for example: what part of the page caused that > > file to be loaded) we could use this information in our packaging system > > to make smarter decisions. > > On Tue, 22 Jul 2014, Ben Maurer wrote: > > > > [Use-case J:] The way we render pages at Facebook [is essentially that > > we] flush sections of HTML with a list of CSS and JS dependencies for > > each section. The HTML is only rendered once these resources are loaded. > > > [Use-case K:] We have a few other use cases for this type of dependency > > (for example, we have a method where you can say "call this callback > > once resources X, Y and Z are requested"). > > On Tue, 3 Sep 2013, Ryosuke Niwa wrote: > > > > [Use-case L:] A web page wants to load and execute a script widget.js if > > the script is already cached in the browser. However, it wants to load > > other essential assets such as images first if it's not already in the > > cache except as long as the user had not started interacting with the > > parts of the page that require widget.js. > > On Tue, 18 Mar 2014, Jonas Sicking wrote: > > > > [Use-case M:] Being able to specify that an image/video should only be > > downloaded "on demand" (aka "lazily"), i.e. when it's in view, or about > > to be in view. Use case is both to lower bandwidth in cases of long > > pages where the user doesn't always scroll to the bottom, as well as > > make sure to fill the network pipe with the most important resources > > first. > > > [Use-case N:] Being able to specify that a stylesheet should not block > > rendering. Use case is to preload stylesheets that will be used by > > content that isn't rendered in the initial view, but that might be > > rendered later. > > > [Use-case O:] Being able to specify some form of prioritization for > > resources like (non-blocking) stylesheets, (non-blocking) <script>s, > > images, video, iframes etc. Use case is making sure to fill the network > > pipe with the most important resources first. Possibly a simple > > prioritization like "needed for initial rendering/not needed for initial > > rendering" is enough, possibly there are needs for more finegrained > > prioritization like "needed for initial rendering/needed for page > > feature X that is commonly used, needed for other". > > On Fri, 30 Aug 2013, Yoav Weiss wrote: > > > > [Use-case P:] download dynamic page components (e.g. maps) only on > > larger devices. > > On Wed, 10 Jul 2013, Kyle Simpson wrote: > > > > [Use-case Q:] I am dynamically loading one of those social widgets that, > > upon load, automatically scans a page and renders social buttons. I need > > to be able to preload that script so it's ready to execute, but decide > > when I want it to run against the page. I don't want to wait for true > > on-demand loading, like when my user clicks a button, because of the > > loading delay that will be visible to the user, so I want to pre-load > > that script and have it waiting, ready at a moment's notice to say "it's > > ok to execute, do it now! now! now!". > > > [Use-case S:] One CMS plugin wants to load "A.js" and "B.js", where B > > relies on A. Both need to load in parallel (for performance), but A must > > execute before B executes. I don't control A and B, so changing them is > > not an option. This CMS plugin [wants] to wait for some > > user-interaction, such as a button click, before executing the code. We > > don't want there to be any big network-loading delay visible to the user > > between their click of the button and the running of that plugin's code. > > > > Another CMS plugin on this same page wants to load "A.js", "C.js", and > > "D.js". This plugin doesn't know or care that the other plugin also > > requests "A.js". It doesn't know if there is a script element in the > > page requesting it or not, and it doesn't want to looking for it. It > > just wants to ask for A as a pre-requisite to C and D. But C and D have > > no dependency on each other, only a shared dependency on A. C and D > > should be free to run ASAP (in whichever order), assuming that A has > > already run [once] some user-interaction that initiates the load of A, > > C, and D. This user interaction may be before the other plugin requested > > A, or after it requested A. > > > > "A.js" can be requested relatively (via a <base> tag or just relative to > > document root) or absolutely, or it might be requested with the > > leading-// protocol-relative from, taking on whatever http or https > > protocol the page has, whereas other references to it may specify the > > prototcol. > > > > These plugins can't guarantee what ID's or classes they use are reliably > > unique without undue processing burden. > > > [I've trimmed the text Kyle wrote here, but also implicit in his > > description, as I understood it, was that A.js should only run once even > > if both plugins tried to load it.] > > > [Use-case T:] I have two different calendar widgets. I want to pop one > > of them up when a user clicks a button. The user may never click the > > button, in which case I don't want the calendar widget to have ever > > executed to render. [...] > > > > It'd be nice if both calendar widgets were built sensibly so that > > loading the code didn't automatically render. One of them IS, the other > > is unfortunately mis-behaving, and it will render itself as soon as its > > code is run. [...] > > > > Furthermore, these two widgets are not "equal". Perhaps one is better > > for smaller browser window sizes, and the other is better for larger > > browser windows. [...] > > > > Regardless, the point is, there's run-time conditions which are going to > > determine if I want to execute calendar widget A or B, or maybe I never > > execute either. But I want them both preloaded and ready to go, just in > > case, so that if the user DOES need one, it's free and ready to execute > > with nearly no delay, instead of having to wait to request as I would > > with only on-demand techniques. > > > [Use-case U:] I have a set of script "A.js", "B.js", and "C.js". B > > relies on A, and C relies on B. So they need to execute strictly in that > > order. [Now], imagine they progressively render different parts of a > > widget. [...] I only want to execute A, B and C once all 3 are preloaded > > and ready to go. It's [...] about minimizing delays between them, for > > performance PERCEPTION. > > > > [For example, one of them might start playing a video, and another might > > introduce the <canvas> slides for that video. You want all of the > > relevant scripts to be run at once, so there's no point where the page > > has a <video> element but doesn't have the <canvas>.] > > On Thu, 11 Jul 2013, Kyle Simpson wrote: > > > > [Use-case V:] you have a string of scripts ("A.js", "B.js", and "C.js") > > loading which constitute a dependency chain. A must run before B, which > > must run before C. However, if you detect an error in loading, you stop > > the rest of the executions (and preferably loading too!), since clearly > > dependencies will fail for further scripts, and the errors will just > > unnecessarily clutter the developer console log making it harder to > > debug. > > > [Use-case W:] some developers have even requested to be able to stop the > > chain and prevent further executions if the script loads, but there's > > some compile-time syntax error or run-time error that happens during the > > execution. For them, it's not enough for B to simply finish loading > > successfully, but that it must fully execute without error. > > On Sun, 14 Jul 2013, Kornel Lesiński wrote (trimmed): > > > > [Use-case X:] not all dependencies are JS files, e.g. authors use > > plugins to load template files, JSON, images, etc. > > > > [Use-case Y:] not all dependencies are usefully satisfied immediately > > after their JS file is loaded, e.g. some libraries may need asynchronous > > initialization. > > > > [Use-case Z:] Another common kind of dependency scripts have is presence > > of certain element in the DOM, e.g. `dropdown-menu.js` may require `<nav > > id="menu">` to be in the document _and_ have its content fully parsed > > before the script can run. > > On Tue, 27 Aug 2013, Ian Hickson wrote: > > > > Jake also mentioned these requirements: > > > > | - Provides an adoption path for browsers that don't support the new > > | feature (happy for the fallback to be blocking document-order > > | execution) > > | - Is discoverable by pre-parsers (so async=false and old-IE's > > | readystate methods aren't enough) > > > > And Kyle mentioned this scenario that we need to handle as well (not > > strictly a use case, more a variant on the above use cases): > > > > > I want to preload a script which is hosted somewhere that I don't > > > control caching headers, and to my dismay, I discover that they are > > > serving the script with incorrect/busted/missing caching headers. > > > Incidentally, I don't think the use case is or should be "make it > > possible to write a performant script loading library". I think the use > > case should be more like "make it unnecessary to ever write a script > > loading library". > > > > Bearing those use cases and requirements in mind, here's the feedback > received since the last proposal: > > On Thu, 29 Aug 2013, Jake Archibald wrote: > > On 27 August 2013 22:55, Ian Hickson <ian@hixie.ch> wrote: > > > On Tue, 9 Jul 2013, Bruno Racineux wrote: > > > > > > > > I would also strongly favor restoring the previous spec portion of > > > > 'defer' which allow to have defer on inline script blocks (i.e. if > > > > the src attribute is not present). I don't know why this html4 > > > > functionality was removed from html5? > > > > > > Well, primarily because basically nobody implemented it, but also, > > > because it's not clear what the point is. Why would you need it? > > > > I like my scripts to be inert, as in have no impact on the page (like > > jquery). I use a small inline script to start the work needed for that > > page, meaning I know the starting point for all JS interacting with the > > page is somewhere in an inline script. This is much easier to maintain > > than scripts that trigger themselves ondomready depending on what they > > see in the DOM. > > I agree entirely. But how does this impact defer=""? If the script has no > effect, it really don't matter when it's run. > > Or are you saying you want an inline script to be deferred, so that it > runs after all the external deferred scripts? > > Given legacy implementations and back-compat needs I doubt we could do > this with defer="" at this point, but: > > > In your proposal, does this work?… > > > > <script src="whatever.js" whenneeded></script> > > ... > > <script needs="whatever.js"> > > activateTheAlmightyWebsockets(); > > </script> > > Yeah, we should support that, certainly. > > > > > On Wed, 10 Jul 2013, Jake Archibald wrote: > > > > > > > > If "dependencies" took a CSS selector it could be: > > > > > > > > <script dependencies=".cms-core" src="cmd-plugin.js"></script> > > > > > > > > Now the number of scripts with class "cms-core" can change between > > > > versions of the CMS but the plugin still waits for them all. No ID > > > > generation needed. > > > > > > Using a selector is an interesting idea. > > > > > > It makes it harder to detect and prevent loops, but not fatally so. > > > > I'm not sure it's possible to get into loops with this. I imagined > > dependency resolution to happen once, on element creation or adding to > > document (whichever happens latest). So with: > > > > <script src="a.js" needs="script[src=b.js]"></script> > > <script src="b.js" needs="script[src=a.js]"></script> > > > > …the first script would have zero dependencies, because the selector > > matches zero elements. The second would depend on the first, so the > > execution order is a.js, b.js. The thing I like about the selector thing > > is you can very easily get (almost) async=false behaviour: > > > > <script src="a.js" needs="script"></script> > > <script src="b.js" needs="script"></script> > > <script src="c.js" needs="script"></script> > > Ah. A one-off selector matching seems less interesting. > > With real-time matching, you could do things like: > > <script src="fancytextarea.js" needs="textarea"></script> > > ...and have it get invoked once a textarea appeared, or some such. It > would depend on exactly how the semantics were set up, I guess. > > > > execute() should return a promise that resolves when the script > > successfully downloads and executes. > > I guess we could do that. It's not clear to me what the benefit is. None > of the use cases particularly seem to need it. > > > > Also, should there be an event on the script element when the script has > > downloaded, but not executed (like IE used to have)? > > Similarly here. What's the use case? > > > > > <script> elements also get a new needs="" attribute, which takes a > list > > > of URLs. A <script> won't run (even if you call execute()) until all > the > > > <script src=""> elements referenced by its needs="" attribute are > > > themselves ready to run. For example: > > > > > > <script src="bbbbb.js" needs="aaaaa.js"></script> > > > <script src="aaaaa.js" async></script> > > > > > > > It seems weird to refer to "needs" by url, but still require script > > elements for those needs, I'd rather use IDs (or selectors yey!). > > IDs require more markup to do the same thing: > > <script src="bbbbb.js" needs="a"></script> > <script src="aaaaa.js" id="a" async></script> > > Selectors require more markup or more complex selectors: > > <script src="bbbbb.js" needs="#a"></script> > <script src="aaaaa.js" id="a" async></script> > > <script src="bbbbb.js" needs="script[src='aaaaa.js']"></script> > <script src="aaaaa.js" async></script> > > Not clear to me that either are a win. > > Using URLs has the big advantage that it means you can do this logic > across import documents, too, which will become more interesting when Web > components are used more. > > Having said that, IDs and Selectors do have a few of advantages that > aren't obvious in the above code: they let you reference elements other > than scripts, and they avoid one confusing aspect of URLs which is that > people interpret the following as actually executing a.js even if there's > no <script> that mentions a.js in its src="": > > <script src="b.js" needs="a.js"></script> > > Also, IDs mean you can have dependencies on local script blocks. > > One _disadvantage_ of IDs, though, is that they don't let us dedupe > multiple scripts with the same URL as easily. Though maybe the solution > there is for there to be some attribute (e.g. whenneeded="") that applies > a new rule: if this <script> refers to a resource already mentioned by > another <script>, then ignore this <script>, or make references to this > <script> defer to the previous one. > > Then again, for scripts, maybe we can just tell people to use modules? > > One thing worth bearing in mind is that if we use a different syntax for > "import" in ES6 modules than for needs="" (or whatever we call it) in > HTML, then you won't be able to simply promote "import" to the attribute, > you'll have to manually figure out what it means. > > > > Also, I'm not sure we need to deal with the case above, where the script > > with the dependency appears in the DOM prior to the script it depends > > on. Avoiding this avoids the circular dependency problem (see my > > selector example above) > > Circular dependencies are relatively easy to break. > > Not having requirements on order means that you can import multiple > documents that each invoke script, and not have to worry about getting the > order exactly right. > > > > Does a script with "needs" but not "async" block rendering? I hope not. > > needs="" in this proposal implies async-ish behaviour. We want to move > away from anything that blocks. > > > On Thu, 29 Aug 2013, Glenn Maynard wrote: > > > > > > <script whenneeded="jit"> is a special mode where instead of running > > > once the script's dependencies are met, it additionally waits until > > > all the scripts that depend on _it_ are ready to run. ("Just in time" > > > execution.) (The default is whenneeded=asap, "as soon as possible" > > > execution.) > > > > This mode seems to be specifically for [use case U.] > > > > This one seems uncommon, and less like a dependency use case than the > > others. How often is this wanted? Is it too inconvenient to just mark > > them all @whenneeded, and say something like: > > > > document.querySelector("#C").execute(function() { > > A.render(); > > B.render(); > > C.render(); > > }); > > > > That does require the modules render in a function, and not when the > > script is first executed. I don't know how much of a burden that is for > > this case. > > If the scripts were able to expose an API like this, much of the work > we're doing here would be somewhat trivial. My impression is that we're > primarily talking about scripts from multiple third-party vendors that > aren't designed to interact together. There's an example in the use case > description, of a script that shows a video and a separate script that > shows slides synchronised with the video, where one inserts a <video> and > the other a <canvas>, and you want both to happen together so that you > never have one without the other. > > (For a similar reason, I think it's probably not sufficient to just > provide JS modules as the solution to all these use cases. While modules > will certainly eventually become widespread, it will be many years before > all the libraries someone might want to use are in module form. The module > loader API allows authors to write scripts that work around this to some > extent, but that's probably above the skill level for many authors, who > just want to be able to include a script that was written by someone else, > and follow some copy/paste instructions for hooking the script up to the > relevant parts of their page.) > > > > Alternatively, if an event is fired when a script's dependencies have > > been met, then you could mark all three scripts @whenneeded, and call > > (#C).execute() once C's dependencies have been met. > > I don't understand how this would work. Wouldn't this execute the > dependencies before C got loaded? The idea here is to download everything > first, then run it all at once. > > > > Will a @defer dependency effectively defer all scripts that depend on > > it? > > I think, as proposed, it would. However, that's somewhat moot; if you have > a needs="" attribute you're going to be loaded async regardless. > > > On Fri, 30 Aug 2013, Ryosuke Niwa wrote: > > > > > > <script> gets a pair of methods, incDependencies() and > > > decDependencies(), that increase and decrease the dependency count by > > > one, respectively decDependencies() throws if called when the count is > > > zero. If decDependencies() is called and it reduces the number to > > > zero, > > > > I strongly oppose to adding incDependencies/decDependencies. We try not > > to add those pairwise functions as much as possible our C++ code because > > it's really hard to always pair function calls. > > On Thu, 29 Aug 2013, Glenn Maynard wrote: > > > > incDependencies() and decDependencies() may be hard to debug, since if > > somebody messes up the counter, it's hard to tell whose fault it is. A > > named interface could help with this: script.addDependency("thing"); /* > > script.dependencies is now ["thing"] */ > > script.removeDependency("thing"); > > That's better than a count, true. Jake Archibald suggested using promises > -- the API could accept a promise, and should then delay until all the > promises are resolved. Using promises like that is probably the easiest to > not screw up. > > > On Fri, 30 Aug 2013, Glenn Maynard wrote: > > > > I don't like the name "jit", because it already has a different meaning > > when talking about scripting. If this was for CSS or WebVTT or > > something else other than scripts, it wouldn't be as bad... > > Seems reasonable. > > > On Tue, 3 Sep 2013, Ryosuke Niwa wrote: > > > > [...] in order for a dependency specified in "needs" to be satisfied, > > "src" content attribute of the dependent script needs to match the value > > when the script finished running. e.g. myscript.src = null leaves any > > dependency on myscript unsatisfied. > > That seems reasonable. It would be a way to signal a load failure. I'm not > sure how compatible it would be with the ES6 loader though. Once you're > executing code, you're pretty locked-in in terms of what the dependencies > are. We could maybe just consider the dependency to have failed if a > script throws while being evaluated, though. That would match how it works > for new ES6 module scripts. > > > > Also make "needs" IDL property take in any HTML element; e.g. adding an > > image element to "needs" makes the script wait until the corresponding > > image resource is loaded. > > Good idea. In fact, rather than passing just an element, it seems even > more powerful to be able to pass a promise, and to have elements that have > a pending load to have a .loaded attributes that returns a promise. This > would then integrate with Jake's idea of using promises to replace the > dependency API mentioned above. The only problem is that it cuts off the > UA from notifying the source of the promise that the resource is going to > be needed, in the cases where we're delaying resources until needed. > > > On Wed, 2 Oct 2013, Bruno Racineux wrote: > > > > I agree on the verbosity with the double markup. It feels redundant. But > > perhaps so is the repeat of the script name with 'needs="A.js"' for > > every script that would need a dependency. > > Well, presumably we need _some_ markup to indicate what a script depends > on. I don't really know what else it would look like that would be less > verbose than just giving the URL. We _could_ put this information in the > script itself, as in modules, but then you have to fetch the file to know > which file to fetch next, which wouldn't address all the use cases. > > > > But if recompiling or looking up script+chars pairs are much slower than > > deserializing a compiled representation of an entire scripts, it might > > be worth considering down the road... > > Well, again, nothing is stopping browsers from doing this today. > > > > >> I would also strongly favor restoring the previous spec portion of > > >> 'defer' which allow to have defer on inline script blocks (i.e. if > > >> the src attribute is not present). > > > > [...because] many inline functions have no need to run before > > 'interactive' fires. A delay or 'promise' at this stage is too early and > > rarely makes sense. Currently most common domReady hooks rely on > > DOMContentLoaded. As such I see no very good reason to run such plugin > > code prior to DOMContentLoaded. > > > > e.g I want to defer jQuery and I want my small inline script events > > (based on the context of a page) parsed and run post jQuery, right after > > DOMContentLoaded fires. That priority level is something that cannot be > > done right now. > > Would it be sufficient to have the inline script depend on the jQuery > script using needs=""? > > > > Also something that makes me cringe, is the verbose repeat of long paths > > (just like srcset does) > > Well we either have to repeat something, or do it from script. > > I toyed above with using IDs or selectors, and it's at least as bad as > paths, IMHO. The main disadvantage of URLs is that it only lets us do > scripts. I discuss this more after the feedback, below. > > From script, there's basically two approaches that make sense, I think. We > could add dependencies using promises, and we could have some elements > expose an interface of sorts specifically for this purpose. The advantage > of the latter is that it would let the browser support "whenneeded" > semantics, loading images and style sheets only when a script needs them. > > > > If you need dependencies, then how about something shorter like a > > dependency Index, with a 'preload' semantic on top of the > > lazyload+postpone proposal, like: > > > > <script src="jquery.js" preload branch="A1"> > > <script src="plugin1.js" preload branch="A2"> > > > > <script src="plugin2.js" preload branch="A2"> > > > > <script> > > E('plugin2.js'); > > </script> > > > > i.e. The browser would takes the letter as indicator of a dependency > > tree branch, and follow along it's priority number to determine the > > dependencies to load when-needed. Or perhaps using the id attribute with > > the letter+number syntax form. > > That seems rather convoluted. It's also hard to use this kind of scheme > when multiple authors are sharing a page without knowing about each other > (e.g. when you import two unrelated libraries). > > > > My worry with [the concept of 'dependency' attributes and relying or > > asking to the browser to handle it through markup], is a performance > > caveat and potential increasing bloat in pages, vs properly isolating > > what's needed on a per page basis. Platform like wordpress or drupal, or > > poorly written plugins in any platform, already too often add scripts > > globally to all pages, making pages an increasing piece of FUBAR script > > bloat. With the 'whenneeded' concept, it can become quite tempting to > > put many many scripts in the DOM without any guilt, or any sensible > > notion of wether it's actually needed or not for a particular page. > > Fundamentally, any system that allows authors to load things on demand is > going to enable this kind of behaviour. I don't think we should let that > stop us from providing it. The cost of a few imports that don't get > fetched isn't going to have that substantial an effect. > > > On Thu, 29 Aug 2013, Brian Kardell wrote: > > > > "@slicknet: @Hixie @paul_irish this proposal conflates preloading with > > dependency management. I would ignore the latter (as I did originally)." > > > > "@paul_irish: @slicknet @Hixie I feel similarly. Adding HTML semantics > > for dep mgmt duplicates the work of AMD/CJS and ES6 modules. > > #writingemailishard :)" > > > > "@_JamesMGreene: @paul_irish @slicknet @Hixie Agreed, adding script > > dependency management in HTML would be complicated, messy, and verbose." > > > > Me: what they said. > > Well, the idea is not to add something redundant with JS modules; the idea > is to very much integrate with JS modules. However, JS modules alone don't > solve many of the use cases described at the top of this e-mail. It's no > more complicated, messy, and verbose to have HTML support these use cases > natively than it would be to require that authors write scripts to support > them, IMHO (indeed I'd say it's a lot less messy). Do you think we should > just not support some of the use cases in the list above? > > > On Thu, 29 Aug 2013, Nicholas Zakas wrote: > > > > When Kyle and I originally started pushing for a way to preload > > JavaScript many moons ago, the intent was very simple: to allow the > > downloading of JavaScript and execution of JavaScript to be separate. > > The idea being that you should be able to preload scripts that you'll > > need later without incurring the cost of parsing and execution at that > > point in time. There are many examples of people doing this, the most > > famous being the Gmail mobile approach of loading JavaScript in comments > > and then pulling that code out and eval()ing it. > > > > I still feel very strongly that this pattern is a necessary evolution of > > how we should be able to load scripts into web pages. I just want a flag > > that says "don't execute this now" and a method to say "okay, execute > > this now". Allowing that flag to be set both in HTML and JavaScript is > > ideal. > > This is what "whenneeded" and "execute()" provide, in the proposal from > earlier in this thread. > > > > The question of dependency management is, in my mind, a separate issue > > and one that doesn't belong in this layer of the web platform. HTML > > isn't the right spot for a dependency tree to be defined for scripts (or > > anything else). To me, that is a problem to be solved within the > > ECMAScript world much the way CSS has @import available from within CSS > > code. > > What about dependencies on resources that aren't scripts? > > > > I think the use cases other than the initial one (preload/execute later) > > are best relegated to script loaders and are very tied to a current way > > of thinking about loading JavaScript. > > Surely our goal should be to make script loaders unnecessary. > > > > I'd rather provide a simple, low-level piece of functionality that make > > the job of script loaders easier by providing a reliable API and then > > let the dependency management use cases be addressed outside of HTML. > > Can you elaborate on why? > > It seems like having the browser be able to manage the dependencies across > scripts and other resources, and manage the loading of those resources, > would let the browser much better manage the network. There's no way for > an author-provided script loader or other resource manager to know when > other tabs are done loading critical resources so that non-critical > resources can be preloaded, for example. > > > > Other random thoughts: > > > > * "whenneeded" is a very strange name for that attribute. It doesn't > > really tell me anything, as opposed to "preload", "noexecute", or > > "future". How do I know when it will be needed? > > Yeah, whenneeded="" is a poor name. What we really need is a way to give > the element's load policy -- should it precache or not, should it load > asap or be deferred... For some elements, like scripts or style sheets, we > might also want to provide the author control over whether the element > should block subsequent scripts or be asynchronously loaded and applied. > > > On Thu, 29 Aug 2013, Ryosuke Niwa wrote: > > > > To put it another way, I don't see why anyone wants to load a script and > > not execute it other than for the purpose of avoiding the network > > request at a later time. However, if that were the main purpose of > > providing such a functionality, then we also need to address the issue > > of this load request needing to have a lower priority than other load > > requests that are vital for the page. In fact, we might want to avoid > > sending the request of a script file altogether if the user isn't going > > to interact the parts of the page that needs such a script. > > Right. That's what many of the use cases described above are about, which > is how a dependency scheme ended up being part of the proposal in the > first place. > > > On Wed, 4 Sep 2013, William Chan (陈智昌) wrote: > > > > * <link rel=subresource> is great for resource discovery. Given the > > above observation, note that it has some deficiencies. Most obviously, > > it does not indicate the resource type. Browsers today can heuristically > > assign a priority based on the resource type (script/image/stylesheet/ > > etc). Arguably, browsers could just use the filename extension as a hint > > to the resource type, and that'd get us most of the way there. In any > > case, Chromium, when it encounters <link rel=subresource> is going to > > assign the resource load the lowest priority level, and only when the > > parser encounters the actual resource via a <script> tag or something, > > will another resource load be issued with the "appropriate" priority. > > Almost all modern browsers will hold back low priority resource loads > > before first paint in order to get critical scripts and stylesheets in > > <head> ASAP without contention. Anything marked with <link > > rel=subresource> will be considered low priority and in all likelihood > > not requested early. Note that HTTP/2 currently does not support > > re-prioritization (and that feature is being debated), so that means > > that when the resource load for <link rel=subresource> gets issued over > > an HTTP/2 connection, it will have the lowest priority, which is > > probably undesirable. FWIW, I think <link rel=subresource> was a good > > initial start, but suffers from key weaknesses and should be thrown out > > and replaced. > > It seems pretty clear that in general it's better to provide the resource > using its proper form -- be that <img>, <link rel=stylesheet>, <link > rel=icon>, <script>, whatever -- and annotate it with attributes saying > how to load it -- asap, later, when needed, whatever -- than it is to just > generically say "also, I might eventually perchance refer to this URL". > The former would allow the browser to also preparse the contents, e.g. > decoding an image, parsing a style sheet or script, preparsing an HTML > file, and the like. > > > > * Given current browser heuristics for resource prioritization based on > > resource type, all <script> resources will have the same priority. > > Within HTTP/1.X, that means you'll get some amount of parallelization > > based on the connection per host limit and what origins the script > > resources are hosted, and then get FIFO. New additions like lazyload > > attributes (and perhaps leveraging the defer attribute) may affect this. > > With HTTP/2, there is a very high (effectively infinite) parallelization > > limit. With prioritization, there's no contention across priority > > levels. But since script resources today generally all have the same > > priority, they will all contend and most naive servers are going to > > round robin the response bytes, which is the worst thing you could do > > with script resources, since current JS VMs do not incrementally process > > script resources, but process them as a whole. So round-robining all the > > response bytes will just push out start time of JS processing for all > > scripts, which is rather terrible. > > I'm not sure what to do about this exactly. > > > > * Obviously, given what I've said above, some level of hinting of > > prioritization/dependency amongst scripts/resources within the web > > platform would be useful to the networking layer since the networking > > layer can much more effectively prioritize resources and thus mitigate > > network contention. If finer grained priority/dependency information > > isn't provided in the web platform, my browser's networking stack is > > likely going to have to, even with HTTP/2, do HTTP/1.X style contention > > mitigation by restricting parallelization within a priority level. Which > > is a shame since web developers probably think that with HTTP/2, they > > can have as many fine grained resources as they want. > > It's hard to come up with a super fine-grained model that works well with > multiple competing scripts, but we can do better than what we have now, > certainly. It seems we can at least split things into the following > categories, in order of highest priority to lowest: > > 1. resources that are needed and are causing something to block > e.g. <script src="foo.js"></script> > 2. resources that are needed and are neither blocking anything nor > explicitly deferred > e.g. <img src="foo.png" ...> > 3. resources that are needed but are deferred > e.g. <script src="foo.js defer></script> > 4. resources that the browser wants > e.g. <link rel=icon>, <html manifest> > 5. resources that are not yet needed but which the author wants > precached when possible, and which have not been marked deferred > e.g. <link rel=subresource href=...> > 6. other resources > > Is that fine-grained enough? > > We could add other levels too. Maybe a "defer" variant of #5, for > instance: > > <script src="foo.js" whenneeded precache defer></script> > > ...or some such. Or maybe put these things in a "load policy" attribute, > as mentioned above. > > > On Tue, 3 Sep 2013, Ryosuke Niwa wrote: > > > > Use case: A web page wants to load and execute a script widget.js if the > > script is already cached in the browser. However, it wants to load > > other essential assets such as images first if it's not already in the > > cache except as long as the user had not started interacting with the > > parts of the page that require widget.js. > > > > i.e. it (loads and) executes the script immediately when and only when > > the script had already been cached or the user had started interacting > > with the parts of the page that requires the script. Otherwise, the > > script is loaded with a low priority. > > (I've added this one to the list above as use-case L.) > > This seems reasonable. It's kind of opportunistic loading -- it's saying > execute this ASAP, but load it when needed, albeit with precaching if > that is necessary. > > > On Thu, 23 Jan 2014, Bruno Racineux wrote: > > > > What bugs me the most is that a developer's expectations is superseded > > by a feature (the preloader) with different implementation per browser > > engine, that are neither documented (in plain English) nor specced out > > by W3C or WHATWG standards. > > Well the spec is just "parse the document and fetch the URLs you find", > basically. It's a reasonably simple model. The implementation is a bit > more subtle due to off-the-main-thread parsing, document.write(), and so > on, but that shouldn't affect authors all that much. > > > > The lack of such resource control can be a huge waste of bandwidth in > > collateral damage along the benefits of the pre-loader. And in the case > > of responsive images, it prevents us from implementing any simple > > straight forward js solutions. > > I don't know about "straight-forward", but it shouldn't stop you from > making scripted solutions in general. What you can't do from JS is poke > content into the preloader before the script runs, which is what the > preloader is basically for. (This is why the proposal earlier in this > thread includes a dependency model: it lets you provide that information > to the browser before script actually runs, so that the browser can do > useful things with the information it has earlier.) > > > > Anyway, to come to the point of this new parallel thread, this leads me > to > > a suggestion: > > > > 'HIDDEN' as [resources control]: > > > > Could 'resource control' be an associated spec of the 'hidden' attribute? > > The semantics seem compatible with the specs implied by 'hidden'? > > > > Being that: hidden "Specifies that the element represents an element that > > is not yet, or is no longer, relevant" [1]. That suggest it may not be > > needed at all and if so, why load it's associated resources? > > It would certainly make sense for a browser to defer such a load. I don't > know that it would make sense to not load it at all, though. However, it > would make sense to provide controls for that. > > > On Fri, 24 Jan 2014, David Newton wrote: > > > > In addition to overloading `hidden`, it misses the `postpone` use case > > of images that we want to be visible (i.e. not have a `hidden` > > attribute), but not loaded until/unless the user scrolls enough for them > > to be within the viewport. > > Yeah. One thing we could do to make that work would be to have images do > something similar to "whenneeded", where instead of an execute() method to > indicate that they are needed, they trigger that state when the browser > would otherwise try to render them. > > > On Sun, 26 Jan 2014, Qebui Nehebkau wrote: > > > > Basically, - and I'm trying not to over-elaborate here, since my opinion > > isn't really very important - I just mean that I don't think there > > should be any guarantees about how (or whether) browsers will preload, > > nor any specific means of controlling this, because the way resources > > get loaded is not really any of the author's business. I also think that > > the purely presentational choice of a specific image file to represent > > the same content (and, even in the art direction case, they're clearly > > the same content if they can be represented with the same alt text; > > otherwise, there should be multiple img tags) should be specified in > > CSS, not HTML; the argument that preloaders can't consider CSS isn't > > compelling to me, because a browser's choice to preload an image or not > > isn't important enough (or, I think it shouldn't be) to justify > > entrenching in a specification. > > I think this makes sense, insofar as the UA should have the final say (not > the author) regarding how things are downloaded. But on the other hand, > the UA can do a better job if the author is able to provide hints to the > UA about what the best order might be. > > > On Wed, 12 Mar 2014, Jake Archibald wrote: > > > > One of the problems with async scripting is doing something when/if it's > > ready. A complete solution looks like this > > https://gist.github.com/jakearchibald/7662e012462c7537a075, it would be > > a lot easier if we could just do: > > > > thing.loaded().then(function() { > > console.log('loaded'); > > }, function() { > > console.log('failed'); > > }); > > Yeah, it would make sense for objects that have an eventual "ready" state > to have an attribute that returns a promise for the current load state. > That would fit in well with the model of adding dependencies by handing a > promise over. > > > On Wed, 12 Mar 2014, Boris Zbarsky wrote: > > On 3/12/14 7:23 AM, Jake Archibald wrote: > > > > > > If the element hasn't loaded or is loading, vend a promise that > > > resolves/rejects on its load/error event. If the element has fired > > > load/error and isn't loading due to a source change, vend a > > > resolved/rejected promise. > > > > This seems fundamentally racy, no? > > If you're changing what your image is pointing to, then yeah. We could > vend an ImageBitmap object on the promise, I guess, if that's likely to be > a problem. But in practice I don't see why it would be. Who's going to be > using the same image to load multiple images without waiting for one to be > ready? > > > On Wed, 12 Mar 2014, Boris Zbarsky wrote: > > > > I realize no one would write actual code like this; the real-life use > case I'm > > worried about would be more like this: > > > > // img is already loaded sometimes > > // Would like to observe a new load > > var promise1 = img.loaded(); // oops! This will be pre-resolved if > > // we were already loaded, but otherwise > > // will resolve with the new load we're > > // about to start. > > img.src = bar; > > promise1 would be rejected as soon as you set 'src' if it hadn't loaded > yet. If it had loaded, it would resolve straight away. In the latter case, > you might observe either the old image or the new one, there's no way to > know for sure. > > This is certainly bug-prone and I'm sure people will get it wrong and > it'll usually work but sometimes not, due to the race. However, it's a > logic error -- you're always observing the first load, not the second. It > will always resolve straight away, either aborting or succeeding based on > whether the original load is aborted or had completed. > > > On Wed, 12 Mar 2014, Domenic Denicola wrote: > > > > With promises you should only ask for the "loaded" promise *after* > > setting `src`; anything you retrieve before that represents a previous > > load. Except, I suppose, for the base-case of images with no src, > > transitioning to having an src? Or are they considered to have e.g. > > loaded `about:blank` already? I.e. what should this do? > > > > var img = document.createElement("img"); > > var promise1 = img.loaded(); > > img.src = "foo.png"; > > var promise2 = img.loaded(); > > I think we should spec this case to reject promise1, since the image is > not .complete. I definitely don't think we should say that promise1 waits > for the src to be set. > > > > // (4) or it could be rejected with an "InvalidStateError" saying you > > // can't wait for the loading of a non-src'ed image. > > This would be a perfect case for a sync exception, rather than a rejected > promise, IMHO. Throwing an exception synchronously would clearly indicate > to the programmer that the code is Just Wrong. > > > On Fri, 14 Mar 2014, Jake Archibald wrote: > > > > Problem: Initialising an app after a non-module script > > Solution: https://gist.github.com/jakearchibald/000ab94ad9fa5cfe62a8 > > This seems simpler: > > <script src="/whatever.js" id=whatever async></script> > <script type=module needs="whatever"> > import app from "myapp"; > app.init() > </script> > > We would have to make sure, though, that needs="" blocked the execution of > the script _after_ the script was parsed to pull out 'import' statements. > > > > [Use-case Q:] Want to avoid executing a social-media script until the > user > > give some intent, although the script should be preloaded. > > Solution: https://gist.github.com/jakearchibald/dd25f0f2cf47bf2ab93e > > This seems simpler: > > <script id="socializor" src="http://myface.com/socializor.js" > whenneeded></script> > <script> > document.ready.then(function () { > document.querySelector('.social-button').addEventListener('click', > function(event) { > document.scripts.socializor.execute(); > event.preventDefault(); > }); > }); > </script> > > > > [Use-case S:] One plugin wants to execute "A.js" and "B.js" in order > > following an interaction. Another wants to load "A.js" then "C.js" & > "D.js" > > in either order. "A.js" should only execute once. Scripts aren't written > as > > modules and out of developer's control. > > Solution: https://gist.github.com/jakearchibald/120309d88a8bf025e92e > > That's a lot longer than the original proposal's solution. Also it > requires the plugins to know about each other. Also it uses markup. If we > can use markup, then the solution becomes even simpler than the original > proposal's solution, IMHO: > > <script src="A.js" whenneeded></script> > <script id=B src="B.js" whenneeded needs="A.js"></script> > <script id=C src="C.js" whenneeded needs="A.js"></script> > <script id=D src="D.js" whenneeded needs="A.js"></script> > > // CMS plugin 1 > <script> > document.querySelector('.button').onclick = function(event) { > document.scripts.B.execute(); > } > </script> > > // CMS plugin 2 > <script> > document.scripts.C.execute(); > document.scripts.D.execute(); > </script> > > This is dramatically simpler than the solution presented above. > > > > [Use-case T:] Preload 2 scripts, execute one or the other on particular > > interactions > > Solution: Same as Q. > > Similar to Q, I guess, but presumably not identical. Q only has one > script. Again, though, I don't really see why we'd want something as > complicated as suggested in that gist, when we can just have two script > elements and call "execute()" on the one we want. > > (I note here that actually my solutions to Q and T here and in my e-mail > from last year actually make the mistake of not distinguishing scripts > that should be preloaded from those that should not. We probably need an > explicit way to control the load policy here, as mentioned earlier in > this e-mail.) > > > > [Use-case U:] "A.js", "B.js", "C.js" - load them in parallel, execute in > > order, only execute when all have preloaded. > > Solution: https://gist.github.com/jakearchibald/5898e3a4fce62579d75b > > At a glance I've no idea what this script is doing. I don't think we want > to be requiring that authors follow this kind of convoluted code to do > something as simple as use case U. Recall that with the whenneeded=jit > proposal and with needs="" taking URLs, this was as simple as: > > <script src="/a.js" whenneeded=jit></script> > <script src="/b.js" whenneeded=jit needs="/a.js"></script> > <script id=c src="/c.js" whenneeded=jit needs="/b.js"></script> > <script> > // when we need it... > document.scripts.c.execute(); > </script> > > > > [Use-case V:] As U, but don't need to wait for all before executing. > > Stop executing if any script fails to load. > > Solution: https://gist.github.com/jakearchibald/ea7583d50bf3b46395e0 > > It's not at all clear to me how a random author is supposed to figure out > the difference between your solution to U and to V. It seems like any > solution we provide for this should be way more straight-forward. > > The solution using the "whenneeded" proposal is the same as for U but with > the "=jit" bit removed. I think we should probably make this more clear, > e.g. making it policy="precache late-execute" vs policy="precache > early-execute" or some such, but that's a far cry from going all the way > to requiring that authors reduce arrays of promises. > > (Though to be fair, the solution I proposed to V doesn't actually go as > far as the use case asks for, since it doesn't abort later loads. I think > I intended tho onerror function in that solution to manually go in and > stop the loads, but I'm not sure what exactly one could do to do that, > since they're trying to preload, at least. Maybe we should just require > that UAs be clever enough to notice that if they have aborted execution of > a particular dependency chain, they should abort the loads too if they > have no reason to think the loads will be useful later.) > > > > [Use-case W:] As W, but break on execution errors > > Solution: https://gist.github.com/jakearchibald/1b12a0e5414a69d9350f > > That's WAY more complicated than the whenneeded="" proposed solution. > > > > [Use-case X:] Loading non-js dependencies > > Solution: Use XHR + preload for prescanner > > XHR makes it hard to hook into the prioritisation system, but yeah. > > > > [Use-case Y:] Some libraries may need async initialization. > > Solution: These libs should provide a ready promise. > > Agreed, though I would just have them hand that promise to the <script> > element so that it hooks into the rest of the dependency system. > > > > [Use-case Z:] Wait on existence of particular element before executing > > script > > Solution: Either put the <script> that handles script loading after the > > element in question, or use mutation observers > > Yeah. > > > On Sat, 15 Mar 2014, Jake Archibald wrote: > > > > It's everything we need but perhaps not everything we desire. Last time > > we went round with script loading the proposal for high-level dependency > > declaration got weighed down by use-cases that should have been left to > > lower level primitives. These are the lower level bits, something higher > > level is still possible. > > It's not clear to me that promises really are the right "low-level" bits > here. I think we need something lower than those that lets the JS module > system, the HTML Imports system, the <script needs> system, and any > promise-based dependency system all interact in one coherent way. > > > > For legacy-free project, modules + promises for non-script assets are > > the answer. > > How do modules solve the problems discussed here of defining when things > should preload, wait til they're needed, etc? > > > On Fri, 14 Mar 2014, Kyle Simpson wrote: > > > > I'd also like to make the observation that putting <link > > rel=preload>.loaded() together with <script>.loaded(), and indeed > > needing a promise mechanism to wire it altogether, is a fair bit more > > complicated than the initial proposals for script preloading use-cases > > from the earlier threads over the last few years of this list. > > > > For one, we're talking about twice as many DOM elements. For another, > > there's a seemingly implicit requirement that we have to get both ES6 > > promises and DOM promises to land for these suggested approaches to > > work. I don't know if that's already a guarantee or if there are some > > browsers which are possibly going to land DOM promises before ES6 > > promises? If so, ES6 promises become the long pole, which isn't ideal. > > > > Lastly, I'd observe that many of the arguments against the > > original/previous script preloading proposals were heavily weighted > > towards "we don't like script loaders, we want to make them obsolete, we > > need simple enough (declarative markup) mechanisms for that stuff so as > > to make script loaders pointless…" > > > > At one point the conversation shifted towards ServiceWorker as being > > "the answer". When we explored the use cases, it was my impression there > > was still a fair amount of non-trivial code logic to perform these > > different loading cases with SW, which means for the general user to > > take advantage of such use-cases, they're almost certainly going to need > > some library to do it. > > > > I can't imagine most end-users writing the previously-suggested > > ServiceWorker code, and I'm having a hard time believing that they'd > > alternatively be writing this newly suggested promises-based loading > > logic all over their pages. In either case, I think for many of the > > use-cases to be handled, most general users will need to use some > > script-loader lib. > > > > So, if this .loaded() DOM promises thing isn't the silver bullet that > > gets us to "no script loader utopia", then I don't see why it's > > demonstrably better than the simpler earlier proposals. > > > > Moreover, the earlier proposals relied on the browser having logic > > built-in to handle stuff like "download in parallel, execute serially", > > which made their interface (the surface area users needed to work with) > > much less than either the Promises here or the ServiceWorker before. > > > > What you're implicitly suggesting with both sets of suggestions is, > > let's make the user do the more complicated logic to wire things > > together, instead of the browser doing it. Why? > > > > Why isn't putting preloading into existing <script> elements (whether > > exposed by events or by promises) better than splitting it out into a > > separate element (<link rel=preload>) and requiring a third mechanism > > (promises) to wire them up? > > I have to agree with Kyle here. > > > On Sat, 15 Mar 2014, Jake Archibald wrote: > > > > Yep, markup is important for preparsers. <module>/<script type="module"> > > & ES6 modules give us a solution for the future. > > I don't really understand how. Modules offer one small part of what we're > talking about here, but not much more. They don't offer a way to declare > the dependency tree before the files are fetched, there's not really a way > to concatenate modules without putting them in some other file like a ZIP > file or an HTML Import, there's no way to delay the load or execution of a > module until it's needed as far as I can tell, there's no way for modules > to be declared as dependent on other resources, etc. I think modules will > be an important part of this solution, but I don't think they are, on > their own, a complete solution to the use cases presented. > > Certainly without a lot of rather complicated code. > > > On Tue, 22 Jul 2014, Ben Maurer wrote: > > On Tue, Jul 22, 2014 at 10:26 AM, Ian Hickson <ian@hixie.ch> wrote: > > > On Mon, 21 Jul 2014, Ben Maurer wrote: > > > > > > > > (1) Allowing the user to specify parameters to Fetch. For example, a > > > > user could say: > > > > > > > > <script src="/my.js" params="{'headers':{'myheader':'value'}}" > > > > id="myscript" /> > > > > > > > > This would allow the user to make any type of request customization > > > > that they could for a direct call to Fetch. > > > > > > I think this would make a lot of sense. It's similar to earlier > > > suggestions for controlling "Referer" on a per-link basis, but more > > > generic. The problem I had with the proposals for controlling Referer > > > was that while there's an obvious way to do it for <script> and > > > <link>, it quickly starts breaking down don't have a 1:1 mapping of > > > URL to element. For example, how do you get the fetches of URLs in a > > > style sheet? Or of the poster frame of a <video> element? Or > > > EventSource objects? Should we just have different ways to set the > > > options for each one? > > > > I guess the upshot of of doing this through fetch is that once we added > > the ability to specify fetch metadata it would hopefully reduce the need > > for future modification of all the different ways one could load an > > object. > > http://lists.w3.org/Archives/Public/public-webappsec/2014Jan/0129.html > > suggests the idea of using fetch for stylesheet subresources in the > > context of subresource integrity. > > Yeah. I think it makes sense to expose a Request object once one is > underway, and a RequestInit object (probably in the form of a JSON-encoded > content attribute?) to configure it, at least for the main resources. > > I'm not sure how to handle elements with multiple resources, e.g. <video > poster> or the new <picture> stuff. > > > > > > (3) Allowing the user to construct a script or stylesheet from a > > > > fetch. Complex sites often want precise control over when scripts > > > > and stylesheets are loaded and inserted into the DOM. For example, > > > > today inserting a stylesheet blocks the execution of inline script > > > > tags. Some sites may know that this dependency is not needed. > > > > > > This specific use case is something I'm already working on. My > > > long-term plan is to integrate something like this with the HTML > > > Imports dependency model and the ES Module dependency model so that we > > > have a single model that can handle everything. > > > > This sounds interesting. The way we render pages at Facebook seems very > > similar to what you are suggesting. We essentially flush sections of > > HTML with a list of CSS and JS dependencies for each section. The HTML > > is only rendered once these resources are loaded. In essence, it seems > > like we're doing inline HTML imports. We have a few other use cases for > > this type of dependency (for example, we have a method where you can say > > "call this callback once resources X, Y and Z are requested". > > Presumably the callback part of this could be done using promises if we > expose promises for everything that can load. > > (I've added the above, as well as some other use cases you listed, into > the use case list at the top of this e-mail, as use cases I through K.) > > > > > > var myfetch = window.fetch(...); > > > > myfetch.then(function(resp) { > > > > document.body.appendChild(resp.body.asStyleSheet()); > > > > }); > > > > > > > > By calling asStyleSheet, the user could preserve the devtools > > > > experience that exists today while still having complete control > > > > over the fetching of their content. > > > > > > We could do this too. It's mostly just a convenience method, right? > > > > One advantage of doing this is that if there is some use case a site has > > that isn't met by the dependency model they can still manually separate > > the fetch of an object from its insertion into the DOM. Giving the > > author the freedom to manually separate the fetch of a resource from > > it's use in the document gives them a powerful ability to experiment > > with different approaches to resource loading. asStyleSheet/asScript > > wouldn't merely be a helper method to construct a stylesheet from a > > given string of text. For example, line numbers in developer tools (or > > JS stack traces) as if they were loaded directly from the given URL. A > > browser might decide to parse the JS/CSS in a background thread as soon > > as the document was fetched (or cache a pre-parsed version of the file). > > If we provide a way to not apply the resource until it's "needed", doesn't > that just take care of the above automatically? I'm not sure I understand > precisely the use case here. > > > > This model also might be beneficial for sites that were written prior to > > the dependency model you are describing. For example, at Facebook we > > already schedule JS/CSS loading ourselves. This API would allow us to do > > things like fetch a CSS stylesheet without blocking inline script > > execution that follows while making a minimal number of changes to the > > site. > > Can't you do that already? I'm not really following what you need to do > here that you can't already do. > > > On Tue, 22 Jul 2014, Boris Zbarsky wrote: > > > > One issue worth considering here: there are various situations (CSP, > > extension) in which a browser would like to know what sort of resource > > is being loaded, or more precisely how it will be consumed, before > > loading it. > > This argues strongly for either reusing the existing loading mechanisms > for preloading, or at least for providing that context information in the > preload request. > > > On Tue, 22 Jul 2014, Ben Maurer wrote: > > > > To follow this up with a concrete suggestion: > > > > var myfetch = window.fetch('my.css', {'fetch-as': 'stylesheet'}); > > myfetch.then(function(resp) { > > document.body.appendChild(resp.body.asStyleSheet()); > > }); > > > > You can only call asStyleShet if fetch-as=stylesheet. Passing this > > parameter would cause the browser to do all the things it would do if it > > were fetching a stylesheet. For example, it would specify an accept > > header of text/css unless otherwise specified. It would request at the > > same priority as the browser requests other stylesheets (again, unless > > overridden with a yet-to-be-defined syntax). Rules around CORS and > > tainting would be identical to a normal stylesheet fetch (namely that > > you could call.asStyleSheet on a request to a different origin but it > > would have whatever restrictions the browser has on stylesheets from > > different origins). Links in the stylesheet would be interpreted > > relative to the URL used to load it, etc. > > > > Essentially, fetch-as=stylesheet would be the canonical definition of > > the browser's behavior around loading stylesheets. It would define the > > behavior if the proposals I suggested in the original email were > > implemented -- namely if the user chose to pass parameters to the fetch > > algorithm or if the user accessed the result of a stylesheet's fetch. > > > > Boris, Will -- would this setup address the concerns you have about the > > problems websites that use XHR to load resources encounter? > > How would this interact with th ES6 module loader? Would fetch() be above > or below it? > > > On Wed, 30 Jul 2014, Ben Maurer wrote: > > On Wed, Jul 30, 2014 at 11:35 AM, Ian Hickson <ian@hixie.ch> wrote: > > > On Wed, 30 Jul 2014, Anne van Kesteren wrote: > > > > > > > > it would be desirable to have Accept / Accept-Language be set by > > > > APIs, such as <img>. XMLHttpRequest already does this (unless a > > > > developer added those headers), see http://xhr.spec.whatwg.org/ > > > > > > > > If we are eventually going to expose something like a "Fetch" object > > > > for each API that can issue a fetch it would seem best if these > > > > details were defined at the API-level. > > > > > > > > I guess for now I'll add some notes to the network-level bits of > > > > Fetch to indicate Accept / Accept-Language cannot be set at that > > > > point by the user agent. > > > > > > There's three ways that I see: > > > > > > 1. Expose it on a "fetch" object available from all the places that > can > > > do fetches. (HTMLImageElement, XMLHttpRequest, StyleSheet, etc) > > > > > > var img = new Image(); > > > img.src = 'foo.png'; > > > img.fetch.doWhateverWithTheAcceptHeader('foo'); > > > > At what point is the fetch actually being initiated? It's possible that > > fetch will offer some things which can be done post-request (eg, > > dynamically changing the spdy priority), but the accept header may need > > to be specified pre-request. > > The fetch wouldn't be initiated until the event loop spun, but other than > that, it's hard to predict exactly when it'll be done in the general > sense. Depends on things like the load policy. > > > On Thu, 31 Jul 2014, Anne van Kesteren wrote: > > > > Probably not before the end of the current task. The only exception to > > that is EventSource and maybe WebSocket, but they can have a constructor > > argument of sorts to make this work I guess. > > WebSocket probably won't use fetch. Good point about EventSource though. > If we want to add requestInit settings for that, please file a bug. > > > On Thu, 7 Aug 2014, Ilya Grigorik wrote: > > > > Latest draft: https://igrigorik.github.io/resource-hints/ > > The preconnect, preload, and prerender link types seem important to define > in more detail, I'm glad you are doing this work. I suggest that for > things like scheduling and prioritisation, we reuse the proposals that > this thread end up at (whatever that is, be in the proposal below or > something evolved from that). > > Are there requiirements and use cases that you need to address that aren't > covered by the proposals in this e-mail? I think I covered them all, but > I'm not 100% sure. > > > On Fri, 8 Aug 2014, bizzbyster@gmail.com wrote: > > > > +1 to breaking the dependency between fetching the resource and how it > > is later used in the document. This type of “late binding” enables many > > page optimizations. > > Does anyone have a concrete depiction of this use case so we can evaluate > the proposal in this thread againt the use case? > > > On Mon, 11 Aug 2014, Ilya Grigorik wrote: > > > > Will this result in API that extends beyond import and ES6 module use > > cases? For example, I want to have the building blocks that will allow > > me to declare these dependencies on my own: "these two images before > > that script, but after this stylesheet, oh and that stylesheet should > > have higher priority than"... We can express all of this in HTTP/2, and > > I'd like that to be surfaced to the developers as a first class API. > > That's my goal. I don't know if the ES modules spec will be extended > sufficiently to support that, but I hope so. The alternative is that we > have two mostly redundant dependency systems, which I assume wouldn't be > popular with browser vendors. > > > > I'm looking at the gliffy, but having a hard time understanding how this > > will work... It's murky to me how I'd declare a resource, specify its > > priority relative to others, and/or add a dependency. Any other (more > > complete) examples, by any chance? :) > > Check out the examples at the bottom of this e-mail, let me know if they > answer your question, let me know how they look! > > > That concludes the feedback that was sent on the previous proposals. > > There's also some other developments that are relevant. > > There's JavaScript's modules, as mentioned earlier. Modules introduce > their own dependency system. It would probably behoove us to integrate > them with whatever we have for making scripts depend on images and so on. > > HTML Imports are also coming along, and they also have a dependency > system. Again, we should probably make sure that this integrates with the > JavaScript modules dependenc system and whatever we come up with to > address the use cases listed earlier. > > There's a bug tracking the merging of the dependency systems: > > https://www.w3.org/Bugs/Public/show_bug.cgi?id=25715 > > I also brought this up in es-discuss recently, in a series of e-mails on > the subject. > > Before we can come up with a coherent overarching model, though, we need > to know what precisely it is we need to address the use cases in this > e-mail. Let's first look at the dependency tree aspect. > > There's a variety of objects that one might want to depend on: > > - JS modules > - <script> elements > - <img> elements > - <video> elements > - promises > - inline style sheets > - external style sheets > - HTML imports > - etc > > Some can be identified by ID, some by URL, some by element or object, > others by some sort of name. > > They don't all need to be identifiable from an attribute. For example, I > don't think we need to make it possible for a <script> to depend on a > promise declaratively. It's ok if depending on a promise can only be done > via an API call. > > Some of these are somewhat special. For example, if we want multiple > <scripts> pointing to the same src="" to be deduped, then the dependency > mechanism needs to be based on URLs in some way (and presumably > pre-redirect URLs otherwise we make the dependency mechanism depend on > network latency). HTML imports also probably have to be URL-based, because > the actual Document object that an HTML import imports doesn't exist until > the import has been started. > > Modules make this a little more complicated, in that there's a desire that > modules be named so that you can e.g. import "jquery" and be safe in the > knowledge that jQuery will be imported regardless of its local filename, > assuming that it has been previously declared. > > In HTML imports, the mechanism to identify another HTML import to depend > on is <link rel=import href="...">. This only gets invoked once the > resource has already been fetched. HTML imports are identified by URL. > That URL has to be deduped -- each master document only imports each > URL once. > > In JS modules, there's syntax in the script itself, which works similarly. > The syntax identifies modules using a string. The script is first parsed, > the dependencies are identified, they are fetched, this happens > recursively, and then the scripts are evaluated. > > CSS similarly has @imports, though those are currently _not_ deduped. We > should probably change that, so that at least CSSRuleLists are deduped. > Alternatively, we can have a feature that forces deduping (or vice versa). > > To avoid the latency of a round-trip per dependency level, it would be > good if the dependencies could be predeclared, even for e.g. CSS or JS > modules, where you would normally only mention the dependency when you > import it. > > Some dependencies have a presence in the same document, or can have a > presence without any particular difficulty. For example, <script src=""> > elements (not modules) all get listed in the document, even if they depend > on each other. However, not all dependencies are this way. For example, > ES6 modules can reference modules that aren't otherwise mentioned. > Similarly, style sheets can @import other sheets, but if you <link > rel=stylesheet> those sheets in the parent document, you're going to > change the cascade in probably noticeable (page-breaking) ways. > > So if we have a way to declare dependencies declaratively in the markup, > we have an interesting mixture of requirements, even if we assume that > we're not going to be able to reference things like promises declaratively > and can safely leave those to an API: > > - being able to reference specific elements (e.g. depending on an inline > script or style block) > > - being able to reference specific URLs in the context of specific > handling (e.g. "this URL is an ES6 module that someone will import", or > "this URL is a CSS file that someone will try to @import") > > This suggests either that the dependency mechanism needs to be able to > take different types of identifiers (e.g. element IDs or selectors on the > one hand, and URLs+contexts on the other), or that we need to have two > ways to declarare dependencies, e.g. two attributes (one for IDs, one > for URLs+contexts), or that we should rely on rel=preload/rel=subresource > to handle the second type. > > Here's how these three options could look, assuming that bar.css is a CSS > file that foo.css imports, and "baz" is the ID of an element that this > style sheet depends on (maybe a <script> element). > > * different ways we could jam all this into one attribute: > > <link rel=stylesheet href="foo.css" dependson="bar.css as css, baz"> > > <link rel=stylesheet href="foo.css" dependson="css:bar.css baz"> > > <link rel=stylesheet href="foo.css" dependson="bar.css css; #baz"> > > * different ways we could have multiple attributes to split this > information out into: > > <link rel=stylesheet href="foo.css" > dependson-id="baz" dependson-css="bar.css"> > > <link rel=stylesheet href="foo.css" > dependson="baz" dependson-href="bar.css as css"> > > * using just IDs and rel=preload or rel=subresource: > > <link rel=subresource type="text/css" href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > <link rel=preload type="text/css" href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > <link rel="preload stylesheet" href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > * reusing the existing mechanisms for each type, but having a way to force > a particular case not to be instantiated ever, because it will instead > be instantiated using the other mechanisms (e.g. @import): > > <link declare rel=stylesheet href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > These all suffer from pretty notable disadvantages: complicated syntaxes > in attributes or complicated multiple-attribute processing models for the > first five cases, having to introduce a whole new way to declare that > something is "CSS" for all but the last two, and lack of a decent > backwards- compatibility story for the last two. The three before the last > one also suffer from not having an obvious way to be extended to > predeclare JS modules. The last one could be extended easily enough: > > <script declare type=module id=jquery href="p/jq/main.js"></script> > <script type=module dependson=jquery> > import $ from "jquery"; > </script> > > We could also introduce a new void element specifically for preloading: > > <preload kind=stylesheet href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > ...where the "kind" is from a list of predefined types that browsers can > preload, like "stylesheet", "module", "script", "image", etc. However, > introducing a new void element is a non-trivial cost. > > Two other problems with the <link> examples above are the duplication of > information because of using id="", and the use of type="", a MIME type, > to identify the resource type, when in practice how to handle a feature > depends on both more than the MIME type (e.g. script program vs ES6 > module) and less than the MIME type (e.g. most image/* types are handled > by sniffing, not based on the type). The declare="" idea avoid this by > reusing the existing mechanisms. > > Merging the <preload> and rel=preload/rel=subresource ideas, one could > imagine a variant that used a different attribute rather than type=, so > that we didn't have to give a MIME type. > > <link rel=preload as=stylesheet href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > ...or: > > <link rel=subresource kind=stylesheet href="bar.css" id="bar"> > <link rel=stylesheet href="foo.css" dependson="bar baz"> > > rel=subresource has the minor advantage of being already implemented in > Chrome, at least. On the other hand, rel=preload is shorter and would let > us use a shorter attribute name (as="" vs kind="" -- as="" doesn't read > well when the link type is "subresource"). Also, "subresource" is much > harder to type, at least for me. If we're left with deciding between > rel=subresource, rel=preload, and declare="", then basically the choice > becomes one of how important backwards-compatibility is: do we want > something we can deploy today, something we can deploy next year, or > something we can deploy next decade? The longer we can wait, the neater > the solution. The sooner we want it, the more we have to compromise. > > The attribute dependson="" could probably also be refined. People have > suggested various names that could work: > > uses="" > needs="" > dependson="" > dependencies="" > subresources="" > import="" > > uses="", needs="", and dependson="" seem more or less equivalent. I'm a > bit dubious about adding an attribute that ends with "on" to avoid the > similarity with event handler attributes. dependencies="" and > subresources="" seem a bit long without giving an obvious benefit. > import="" seems reasonable, but I'm worried that it is too close to the > ES6 "import", CSS "@import", or HTML rel="import", which all have similar > but stronger semantics. Here we're not trying to actually effect an > import, we're just trying to let the browser know that a particular > element is going to come in useful when this element is invoked. > > There have also been some proposals with names that don't really fit > grammatically, like depends="". I haven't considered those further. > > Let's take the above example and add more nodes to the dependency tree, > and see how it looks with the various proposals. > > rel=preload, as=..., uses=... -- the shortest names mentioned above: > > <!-- core files --> > <script src="core.js" type=module id="core"></script> > <link rel=preload as=stylesheet href="core.css" id="core-css"> > <!-- calendar files --> > <link rel=prelaod as=stylesheet href="calendar.css" id="cal-css" > uses="core-css"> > <link rel=preload as=image href="imgs/cal.png" id="cal-sprites"> > <script src="calendar.js" type=module > uses="core cal-css cal-sprites"></script> > > rel=subresource, kind=... subresources=... -- has a nice consistent > symmetry, but man is it annoying to type: > > <!-- core files --> > <script src="core.js" type=module id="core"></script> > <link rel=subresource kind=stylesheet href="core.css" id="core-css"> > <!-- calendar files --> > <link rel=subresource kind=stylesheet href="calendar.css" id="cal-css" > subresources="core-css"> > <link rel=subresource kind=image href="imgs/cal.png" id="cal-sprites"> > <script src="calendar.js" type=module > subresources="core cal-css cal-sprites"></script> > > rel=subresource, kind=..., needs=... -- less annoying, still annoying: > > <!-- core files --> > <script src="core.js" type=module id="core"></script> > <link rel=subresource kind=stylesheet href="core.css" id="core-css"> > <!-- calendar files --> > <link rel=subresource kind=stylesheet href="calendar.css" id="cal-css" > needs="core-css"> > <link rel=subresource kind=image href="imgs/cal.png" id="cal-sprites"> > <script src="calendar.js" type=module > needs="core cal-css cal-sprites"></script> > > rel=preload, kind=..., needs=... -- similar to as/uses: > > <!-- core files --> > <script src="core.js" type=module id="core"></script> > <link rel=preload kind=stylesheet href="core.css" id="core-css"> > <!-- calendar files --> > <link rel=preload kind=stylesheet href="calendar.css" id="cal-css" > needs="core-css"> > <link rel=preload kind=image href="imgs/cal.png" id="cal-sprites"> > <script src="calendar.js" type=module > needs="core cal-css cal-sprites"></script> > > declare="" and needs="", reusing the existing load mechanisms: > > <!-- core files --> > <script src="core.js" type=module id="core"></script> > <link declare rel=stylesheet href="core.css" id="core-css"> > <!-- calendar files --> > <link declare rel=stylesheet href="calendar.css" id="cal-css" > needs="core-css"> > <img declare src="imgs/cal.png" id="cal-sprites"> > <script src="calendar.js" type=module > needs="core cal-css cal-sprites"></script> > > We do use kind="" on <track>, for something similar, which would be an > argument for kind="" over as="". Also, as="", while cute and, in context, > pretty nice looking, does suffer from lack of clarity when used out of > context (plus, consider the confusing with is="" from Web components). > Searching [link element as attribute] is not likely to get you as helpful > a set of results as [link element kind attribute]. It's really tempting to > just use the declare="" attribute, though, since that reuses so much > machinery and avoids having to intoduce kind="" at all. > > It's harder to decide between needs="" and uses="". I think "needs" > probably has a slight edge only because it, to me at least, more strongly > implies that if the load of a dependency fails, the element cannot be > satisfied. uses="" sounds more opportunistic. For now, let's suppose we go > with needs="". > > (As an aside, I should add that I welcome bike-shedding on all these > names. If you have an idea I haven't considered, or a way of looking at > the names that I haven't mentioned here, please don't hesitate to bring it > up. Now's the time, before anything gets implemented.) > > The discussion above suggests that needs="", the attribute that defines > these dependencies, should take a space-separate list of IDs, rather than > URLs. This, though, brings in a new set of subtle questions. When are > these IDs resolved? Should they update dynamically? For consistency with > the rest of the platform, the answer should probably be yes, but this does > introduce "spooky action-at-a-distance" effects, which is unfortunate. For > example: > > <link id=a rel=preload kind=image href="a.png"> > <link id=b rel=preload kind=image href="b.png"> > <script needs="a" ... id="demo"> > ... > </script> > <!-- later... --> > <script> > document.getElementById('a').id = 'c'; > document.getElementById('b').id = 'a'; > </script> > <!-- later... --> > <script> > document.scripts.demo.execute(); > </script> > > Which image(s) should be downloaded? If the browser is aggressively > precaching everything that something depends on, probably all of them; if > it's only fetching what is actually needed when it's needed, presumably > only b.png. ES6 modules make this a bit more complicated too, in that we > don't fully control when the normalize hook is run (which is where we'd > presumably evaluate these mappings). > > In the example above, the <script> element has a "..." in its attribute > list, which brings us to the next and more thorny part of the whole issue: > how to tell the browser that certain resources are only to be loaded when > they are needed. > > Normally, browsers aggressively download everything. > > <link rel=stylesheet href="a.css"> > <img src="a.png" alt=""> > <script src="a.js"></script> > > All three files will be immediately fetched and applied. In fact, it's > worse than that; the style sheet will block the script from executing, and > the script will block the parser from continuing to the rest of the page > (e.g. blocking a subsequent video from starting to play). > > We have ways to prevent the blocking for <script>, namely defer="" and > async="". Their exact behaviour is a little subtle, but, broadly speaking, > the defer="" attribute delays the script until after the page has finished > loading, while the async="" attribute tries to run the script as fast as > possible without blocking. Style sheets don't currently have such a > mechanism, though it has previously been requested. Everything else, more > or less, loads like the async="" behaviour. (All of the above, including > 'defer', also delay the 'load' event.) > > So, to address most of the use cases described at the top of this e-mail, > we need a way to delay the application (and in some cases loading) of > resources until they are "needed", for some definition of needed. > > Some of the use cases also need a way for us to de-dupe references to the > same resource, even across otherwise independent packages. For example, > two separate packages that both want to load jQuery, or two separate style > sheets for different parts of the app that both want to load a "reset" > sheet first. (The CSS case is not spoken about as much as the scripting > case, but it's still real, especially for big applications; see, for > instance, this talk: > > https://www.youtube.com/watch?v=_MD1WQclOJM > > ...about Google+'s rather elaborate system to solve this.) > > In the proposal from last year, I had suggested a whenneeded="" attribute > with three states: today's status quo, a value to delay the load and > execution until the script was needed (indicated by calling "execute()" on > the script element), and a value to delay the load until then and delay > the execution until everything depending on this script itself was also > ready to execute. > > As discussed earlier in this e-mail, however, this is probably > insufficient. We probably need to just expose a way to control the load > policy for an element. > > There's actually two somewhat orthogonal aspects to this. When to download > the resource, and when to execute or apply it. For some resources, when to > download it is irrelevant (e.g. inline scripts), and for some, when to > apply it is irrelevant (e.g. images). > > For downloading, it again breaks down into several somewhat orthogonal > controls - when, and what priority: > > - When to consider downloading it: > - don't wait, just get it immediately > - wait until either it's needed, or nothing that _is_ needed is > being downloaded (precaching) > - wait until it's needed, don't precache > > - What priority to give the download: > - download it as soon as possible > - allow other downloads to take priority > > That sort of maps to three booleans: > > - fetch-asap vs fetch-when-needed > - precache vs no-precache > - high-priority vs low-priority > > ...except that 'precache' requires 'fetch-when-needed'. > > You can alternatively view this as six states: > > - download now at high priority (default) > - download when needed at high priority (fetch-when-needed) > - download when needed at high priority, but allow precaching > (fetch-when-needed, precache) > - download now at low priority (low-priority) > - download when needed at low priority (fetch-when-needed, low-priority) > - download when needed at low priority, but allow precaching > (fetch-when-needed, precache, low-priority) > > For execution/application, there's basically one control, with five > levels (one of which is the equivalent of declare="", from earlier): > > - execute this now, blocking subsequent scripts from executing until it > is done (this describes the legacy behaviour of <script> and style > sheets, and probably shouldn't be exposed. It also overrides the > download behaviour, since when you're blocked you need the resource as > soon as possible and so you won't wait, nor use a low-priority channel) > > - execute as soon as possible after it is downloaded > > - execute as soon as it is needed > > - execute just before anything depending on this is executed > > - don't ever execute this, this is a placeholder for something that will > be referenced from elsewhere that is just being listed here so that we > can preload it and reference it > > The "execute as soon as possible after it is downloaded" and "execute as > soon as it is needed" options would obviously be equivalent when the > download option is "when-needed", but it would _not_ be the same for other > cases (e.g. it controls whether to execute after precaching or not, and > decides when to apply inline resources, which don't get downloaded in the > first place). > > Let's give those five states straw-man names: block, use-asap, > use-when-needed, use-late, and declare. > > The "low-priority" modifier applies to most of these values, and > "precache" applies to all those with fetch-when-needed. Expanding out all > the combinations, using square brackets to show different possible > combinations for the "low-priority" and "precache" values, we get: > > block > use-asap [low-priority] > use-when-needed [low-priority] > use-late [low-priority] > declare [low-priority] > fetch-when-needed use-asap [low-priority] [precache] > fetch-when-needed use-when-needed [low-priority] [precache] > fetch-when-needed use-late [low-priority] [precache] > fetch-when-needed declare [low-priority] [precache] > > These names are quite confusing. In particular, "use-when-needed" alone > makes it sound like it'll be fetched when needed too, not immediately. And > "fetch-when-needed use-when-needed" is pretty horrible for what might be a > pretty common case. (So common the earlier proposal just had a single > attribute for it, whenneeded="".) > > Here's an alternative set of names for the same states. I used "async" for > "use-asap" because that's basically what <script async> today does. The > others are new names. > > block > async [low-priority] > when-needed preload [low-priority] > late-run preload [low-priority] > declare preload [low-priority] > optimistic [low-priority] [precache] > when-needed [low-priority] [precache] > late-run [low-priority] [precache] > declare [low-priority] [precache] > > Here, "preload" means "fetch it ASAP", and "precache" means "fetch it > whenever you want (but not later than when it's needed)". If you had one > of each of these, what order would they load in, assuming all the ones > that are marked as loading only when needed were requested after > everything else had loaded, and that only one resource could be loaded at > a time? It would be something like this, I think: > > block > async > async low-priority > when-needed preload, declare preload > when-needed preload low-priority > late-run preload > late-run preload low-priority > optimistic precache > when-needed precache, declare precache > late-run precache > optimistic low-priority precache > when-needed low-priority precache > late-run low-priority precache > // (pause until they are marked as needed) > optimistic > when-needed, declare > late-run > optimistic low-priority > when-needed low-priority, declare low-priority > late-run low-priority > > (I'm assuming "when-needed" and "declare" have the same importance in > terms of fetching priority.) > > Even when you hide the low-priority lines, it's still a lot: > > block > async > when-needed preload, declare preload > late-run preload > optimistic precache > when-needed precache, declare precache > late-run precache > // (pause until they are marked as needed) > optimistic > when-needed, declare > late-run > > We clearly don't need all of these. In particular, we could merge > "preload" and "precache" so that it's more than a hint, but less than an > immediate request. This gets us down to basically four keywords plus > "async" and "block", which represent the default behaviours for most > interesting elements: > > block > async > optimistic [precache] [low-priority] > when-needed [precache] [low-priority] > late-run [precache] [low-priority] > declare [precache] [low-priority] > > We could also add a flag that says "always force a new download, rather > than deduping", to match current default behaviour of certain features. > Say, "force". > > Here's how these would map to some existing features: > > <link rel=stylesheet>: block force > <style>: block > @import: block force > <script src>: block force > <script src defer>: when-needed precache force; plus, the page load > itself triggers the script as being needed > <script src async>: async force > <link rel=icon>: when-needed precache > <img ...>: async force > <img ... hidden>: async low-priority force > > So for example, if you had a style sheet that you only wanted to apply > when it was needed by some script, but which could be downloaded earlier > if the browser wanted to, and which should be deduped if there are > multiple instances of it loaded, you could declare it as: > > <link rel=stylesheet href="foo.css" > load-policy="when-needed low-priority precache"> > > For resources that don't have an "execute" step, like images, there's no > concept of late execution. For these, optimistic, when-needed, > and late-run are all essentially the same, and "block" doesn't apply; the > choice is really between "fetch now", "fetch soon", and "fetch when > needed", aka async, when-needed precache, and when-needed. > > Again, I'm not a great fan of these names. Bikeshed away. > > > To complement the dependency model and load policy, we need a few > additional features. There needs to be a way to indicate that something > that is waiting to be needed is in fact needed (separate from something > depending on it, that is). So probably a method like .execute(), though > it would be nice to have a name that could apply to all elements. > > The browser also needs a way to say "I need this" for some elements, such > as images -- there, it would kick in when the element is being rendered. > > It would also be nice to be able to have an API that lets you add elements > to another element's dependency list without having to add an ID and add > the element that way. (That would also allow cross-document dependencies, > which might be useful.) Maybe foo.addDependency(). > > To make this all work well with promises, it would also be nice to have an > API to say "add this promise as a dependency". This could be an overload > of the element-receiving one mentioned in the previous paragraph. > Unfortunately, as far as I can tell, a promise breaks the back-channel > logic. For example, consider this case: > > <link rel=stylesheet href="foo.css" load-policy="when-needed" id=a> > <img src="bar.png" alt="..." load-policy="when-needed" id=b> > <script> > links.a.addDependency(images.b); > </script> > > > At this point, ideally, nothing would happen. Now suppose that we told the > style sheet link that it was needed. Ideally, the image would then > download itself also. But in practice, I don't see how we can actually do > that. There's no back-channel along the promise to inform the image that > it should now start downloading itself. > > So I guess for promises as dependencies, we'd only have a way to wait on > the promise, not a way to also trigger the load that the promise > represents. That's probably sufficient for most cases if we also have a > way to add a dependency by ID. > > To help hook into things that use promises, we should presumably have > .loaded and .ready attributes that return promises for when something has > executed/applied (.loaded, similar to onload) and when things are > prefetched and ready to go (.ready). We could pair that last one with a > new event, onready, for those who prefer the event model. > > We also need a way to actually say that things should be executed. For > consistency with onload, I guess a .load() method. > > > > Earlier in this e-mail I mentioned ES6 modules and HTML imports, and > pointed out that they both had dependency systems. If we add in the system > described above for HTML resources, we now have three. I think it's pretty > critical that we not require that browsers implement three redundant > dependency systems. > > Since the more mature of the three is ES6, I've been trying to work on > fitting HTML imports and the system described above into the ES6 module > world. Mostly this just means defining the default behaviour of the ES6 > loader hooks, though there's probably a few things that would need > changing at the ES6 module level to make it fully work (I've been sending > this feedback to the es-discuss list directly for those who want to follow > along over there). > > The main thing that ES6 doesn't currently support is the ability to set > the dependencies ahead of time. The discussion below assumes that that > will be resolved. There's also a minor issue with being able to associate > metadata with a load when it's discovered as a dependency and when its > name is normalised, so that @import rules can work right. > > Modulo those issues, the idea is that any fetch within the context of a > Web page would be added to the ES6 module loader mechanism. > > The "normalize" hook would need a way to reference elements by ID. There's > two obvious ways to do that: support any string that matches an ID as > being an ID, and support any string starting with a "#" as being an ID. > The former is neater-looking: > > <link rel=stylesheet id=foo ... load-policy="when-needed"> > <script type=module> > import styles from "foo"; > </script> > > ...but means that we're now overloading the string passed to import as > multiple things (package locators, IDs, and URLs) in a rather ambiguous > way. So the hash idea is probably better: > > <link rel=stylesheet id=foo ... load-policy="when-needed"> > <script type=module> > import styles from "#foo"; > </script> > > It's important to distinguish this from a URL, though; I don't expect this > to work: > > <!-- in foo.html --> > <link rel=stylesheet id=foo ... load-policy="when-needed"> > > <!-- in bar.html --> > <link rel=import href="foo.html"> > <script type=module> > import styles from "foo.html#foo"; // the #foo here presumably has no > effect? > </script> > > But what should the "#foo" do? Probably it should throw, but we could > imagine maybe having the "normalize" hook wait until the import is done? > > ("normalize", by default, would probably also need to support two other > things: > 1. absolute and scheme-relative URLs > 2. some sort of relative naming for ES6 modules themselves, so that > you can do things like: > import "jquery"; > import "../base/utils"; > import "login/login"; > ...and have it get "jquery.js" from some predeclared registry, > "../base/utils.js" relative to the current script (not the doc!), and > "login/login.js" in the same way. > I'm not really sure how to do the package part of this.) > > The other hooks do pretty much the obvious things, except maybe > "instantiate" which has to figure out how to import something once it has > its MIME type. > > > > Pulling all of the above together, here's the tentative proposal: > > These "loadable" elements: > > <script>, <link>, <style>, <video>, <img>, <object>, <iframe>, <audio> > > ...get the following new attributes: > > needs="" Gives a list of IDs of other elements that this one > needs, known as The Dependencies. Each dependency > is added to this element's [[Dependencies]] in the > ES6 loader. > > load-policy="" The load policy. Consists of a space-separated > set of keywords, of which one may be from the > following list: block, async, optimistic, > when-needed, late-run, declare. The other > allowed keywords are precache, low-priority, > and force. (Maybe we disallow "block" and > "force" since they're for legacy only.) > Different elements have different defaults. > "precache" isn't allowed if the keywords > "block" or "async" are specified, since those > always load immediately. > > load-settings="" A JSON-encoded dictionary to pass to the Request > constructor. > > ...and API: > > .addDependency() Passed a promise, makes this element depend on that > promise. Passed a "loadable" element, does the same > as if that element's ID was mentioned in needs="". > > .load() Mark the element as needed, and apply or execute it > as soon as possible. Returns the new .loaded > promise (any earlier one is rejected). > > .ready Promise indicating calling load() will immediately > apply or execute when load() is called. > > .loaded Promise indicating that the element has applied or > executed. > > .request The current Request object, if a fetch has been > started. > > .needs reflects needs, maybe as a custom object, or > otherwise as a DOMTokenList > > .loadPolicy reflects load-policy, maybe as a custom object, or > otherwise as a DOMTokenList > > .loadSettings reflects load-settings, maybe as a custom object > > These elements can be in six states. The first five are sequential; > elements try to go through them in turn: > > - idle (the initial state at creation time) > - prefetching... > - ready (matches the .ready promise) > - loading... > - loaded (matches the .loaded promise) > > ...and the sixth is "error", meaning something failed permanently. > > Setting src="", or whatever causes the element's state to be reset, > immediately rejects the preexisting .loaded promise and creates a new > one, moving the element back to "idle". > > When an element is created, it's added to the ES6 module registry. (When > one of these elements has its ID or URL changed, its entries in the > registry are updated.) The ES6 LoadModule() operation is called for this > module (that's how it is added to the registry). > > Except if the load policy has the "force" flag, when the element is added > to the registry it's done in such a way as to rely on ES6 deduping. > > An element can be needed. By default it's not, unless it has a load-policy > of "block" or "async". Upon creation, and when its needs="" is changed > while the element is still not ready, or when another element's ID is > changed and that matches an ID in an element's needs="", the element's > [[Dependencies]] list is updated accordingly. > > When an element is marked as needed, all the things in its > [[Dependencies]] get marked as needed also. > > An element in "idle" moves to "prefetching" if the load-policy is > optimistic and the browser has nothing better to do, or the load-policy > has "precache" declared and the browser has nothing to do, or the element > is marked as "needed" somehow. > > An element's "fetch" hook blocks until the element reaches "prefetching". > Once it does, if this is something to download, it creates a Request > object from the load-settings="" attribute and the appropriate URL. For > inline scripts and styles, the body comes from the element. Once the fetch > hook finishes, an event is to be fired at the element. Once all its > dependencies are ready, another event is to be fired at the element. > > When a script is needed, once it's ready, it is EnsureEvaluated() (see > the ES6 spec for details). > > When scripts run, if they throw an uncaught exception then they go to the > "error" state and that prevents any dependencies from resolving. If > something is being loaded and it depends on something that's reached > "error", it aborts loading. Something that depends on a resource in the > "error" state won't load, it'll just transition to "error" straight away. > (Or should it just wait, so you can remove the dependency and unblock it?) > > Changing the "load-policy" doesn't reset the element's state, it just > causes it to resume from its current state with the new policy. If the new > policy is irrelevant (because it applies to a state earlier than the > current one), then nothing interesting happens. For instance, moving from > "block" to "declare" after the file has already been executed does nothing. > > For style elements (and most elements, in fact), the "execute" callback > does nothing. The StyleSheet object is created earlier. The script is > applied once it is both needed and ready (in place of EnsureEvaluated()). > While it is marked with a load-policy of "declare", subsequent fetches of > the same URL in a style sheet context in the same Loader will use the same > file, not refetch it. > > Certain loadable elements can also be told to execute by the browser even > when they are awaiting being needed. Notably, <video>, <audio>, <img>, > <object>, and <iframe> will self-need themselves if the come into view. > > The <link rel=preload> feature is given an attribute, kind="", which it > can use to determine how to parse the file (image, style sheet, HTML > import, JS script, JS module, ...). > > Documents add things like synchronous scripts, style sheets, deferred > scripts, images, etc, to their dependency list while they are loading. > > When a Document loads, all the <script defer> elements are told they > are needed. > > Elements that aren't one of these "loadable" elements that end up being > imported as ES6 modules act as follows: > > - fetch: blocks until the ID is visible > - instantiate: returns the element > > By default, the priority of loads is based on the load policy and whether > the element is needed or not. > > > > Here's how this would handle the use cases listed above. > > > [Use-case F:] A website has a page where media is the primary content. > > It would like to make sure that media is downloaded before JS [e.g. > > flickr, youtube, facebook photos] > > <!DOCTYPE HTML> > <html> > <title>Photo 23 in My Gallery</title> > <body> > <h1>Photo 23</h1> > <p><img src="photo23.jpeg" alt="..."></p> > <script src="more-features.js" load-policy="low-priority"></script> > </body> > > > > [Use-case G:] A website knows there's a piece of Javascript code that > > the user might need if they click on a part of the page. The developer > > would like to have the user download it, but not at the expense of other > > resources. > > <script src="button-reaction.js" id="reaction" > load-policy="when-needed precache low-priority"> > // button-reaction.js defines react() > </script> > <button type=button > onclick="document.scripts.reaction.load().then( > function() { react(); })"> Part of the Page </button> > > > > [Use-case H:] A website is prefetching photos in a photo album and would > > like to make sure these images are lower priority than images the user > > is actually viewing. > > <img src="photo1.jpg" alt="..." load-policy="when-needed precache"> > <img src="photo2.jpg" alt="..." load-policy="when-needed precache"> > <img src="photo3.jpg" alt="..." load-policy="when-needed precache"> > <img src="photo4.jpg" alt="..." load-policy="when-needed precache"> > <img src="photo5.jpg" alt="..." load-policy="when-needed precache"> > > As they come into view, they'll become needed automatically. When they are > not needed, they get precached if that wouldn't get in the way of other > things getting loaded. > > > On Mon, 28 Jul 2014, Ben Maurer wrote: > > > > [Use-case I:] Facebook uses an automated pipeline to decide what CSS/JS > > files to package together. If we could pass a custom header in the > > request for a CSS file (for example: what part of the page caused that > > file to be loaded) we could use this information in our packaging system > > to make smarter decisions. > > <link rel=stylesheet href="..." > load-settings='{ "headers": { "X-Facebook-Blame" : "<head>" } }'> > > ...or some such. The exact syntax of load-settings="" isn't something I've > studied closely. It might make sense to have a more dedicated syntax, > since JSON doesn't work well in HTML attributes due to quoting rules. > > From script: > > var style = document.createElement('link'); > style.rel = 'stylesheet'; > style.href = '...'; > style.loadSettings = { "headers": > { "X-Facebook-Blame" : "<head>" } > }.toJSON(); > > ...or some such. Again, it might make sense to have a dedicated API for > loadSettings which would make this better. Also, medium term, Tab is > working on a StyleSheet object and constructor. > > > > [Use-case J:] The way we render pages at Facebook [is essentially that > > we] flush sections of HTML with a list of CSS and JS dependencies for > > each section. The HTML is only rendered once these resources are loaded. > > We could have dedicated features for this (like making any element depend > on any other, making <div>s whose dependencies aren't met be display:none, > or even having :loaded/:ready pseudo-classes to match elements based on > their dependencies' statuses) but for now, even with the stuff above, the > simplest solution is probably more or less what you'd do now: > > <section hidden class="foobar"> > <!-- this is the section of HTML mentioned in the use case --> > <!-- each dependency is listed explicitly: --> > <script> > // Prior to this we must have loaded a script that defines two > // functions. The first adds style sheets, the second adds scripts. > // They return promises. All the things added by these functions are > // fetched right away. > var promises = []; > // To add a style sheet, you need to know the URL to the sheet, > // any sheets it depends on, and the relative order amongst > // all the sheets in the project > promises.push(addStyleSheet('common.css', [], 1)); > promises.push(addStyleSheet('foobar.css', ['common.css'], 4)); > // To add a script, you just need the URL and its dependencies. > promises.push(addScript('common.js', [])); > promises.push(addScript('foobar.js', ['common.js'])); > var section = document.currentScript.parentNode; > Promise.all(promises).then(function () { > section.hidden = false; > }) > </script> > </section> > > Depending on the size of your total dependency tree, you could predeclare > it in the <head> and just refer to that from import statements, something > like: > > <link rel=stylesheet load-policy="declare when-needed" > href="common.css"> > <link rel=stylesheet load-policy="declare when-needed" > href="foobar.css" needs="common.css"> > <script src="common.js" load-policy="declare when-needed"></script> > <script src="foobar.js" load-policy="declare when-needed" > needs="common.js"></script> > > ...and then later when you send the markup: > > <section hidden class="foobar"> > <!-- this is the section of HTML mentioned in the use case --> > <!-- each dependency is listed explicitly: --> > <script type="module"> > import "./foobar.css"; > import "./foobar.js"; > document.currentScript.parentNode.hidden = false; > </script> > </section> > > If you gave the <link> and <script> blocks IDs, you could refer to them > that way instead, to be more explicit: > > <link rel=stylesheet load-policy="declare when-needed" > href="common.css" id=common-css> > <link rel=stylesheet load-policy="declare when-needed" > href="foobar.css" id=foobar-css > needs="common.css"> > <script src="common.js" load-policy="declare when-needed" > id=common-script></script> > <script src="foobar.js" load-policy="declare when-needed" > needs="common.js" id=foobar-script></script> > > ... > > <section hidden class="foobar"> > <!-- this is the section of HTML mentioned in the use case --> > <!-- each dependency is listed explicitly: --> > <script type="module"> > import "#foobar-css"; > import "#foobar-script"; > document.currentScript.parentNode.hidden = false; > </script> > </section> > > However, giving all the dependencies ahead of time doesn't really scale > for even medium-sized sites, unfortunately. > > > > [Use-case K:] We have a few other use cases for this type of dependency > > (for example, we have a method where you can say "call this callback > > once resources X, Y and Z are requested"). > > Promise.all([X.loaded, Y.loaded, Z.loaded]).then(thisCallback) > > > > [Use-case L:] A web page wants to load and execute a script widget.js if > > the script is already cached in the browser. However, it wants to load > > other essential assets such as images first if it's not already in the > > cache except as long as the user had not started interacting with the > > parts of the page that require widget.js. > > <script id=w src=widget.js load-policy="optimistic low-priority"> > </script> > > <div> <!-- part of the page that needs the widget --> > <script> > var div = document.currentScript.parentNode; > function startWidget () { > document.scripts.w.load(); > div.removeEventListener('mouseover', startWidget, true); > div.removeEventListener('focus', startWidget, true); > }; > div.addEventListener('mouseover', startWidget, true); > div.addEventListener('focus', startWidget, true); > </script> > </div> > > > > [Use-case M:] Being able to specify that an image/video should only be > > downloaded "on demand" (aka "lazily"), i.e. when it's in view, or about > > to be in view. Use case is both to lower bandwidth in cases of long > > pages where the user doesn't always scroll to the bottom, as well as > > make sure to fill the network pipe with the most important resources > > first. > > <img src="..." alt="..." load=policy="when-needed"> > > > > [Use-case N:] Being able to specify that a stylesheet should not block > > rendering. Use case is to preload stylesheets that will be used by > > content that isn't rendered in the initial view, but that might be > > rendered later. > > There's various options here. If we're going to explicitly invoke the > style sheet in due course: > > <link rel=stylesheet href="..." id="mystyle" > load-policy="when-needed precache"> > > // when needed: > document.getElementById('mystyle').load(); > > If we're just hoping it'll be loaded slower, but will be applied > automatically: > > <link rel=stylesheet href="..." id="mystyle" > load-policy="async low-priority"> > > Medium term we might want to consider letting random elements depend on > the style sheet, not just <img>, so that when the elements that do use > this come into view, the style sheet can be forced on (and maybe the > aforementioned pseudo-classes can be used to display:none the element > while it's loading the style sheet or something.) > > > > [Use-case O:] Being able to specify some form of prioritization for > > resources like (non-blocking) stylesheets, (non-blocking) <script>s, > > images, video, iframes etc. Use case is making sure to fill the network > > pipe with the most important resources first. Possibly a simple > > prioritization like "needed for initial rendering/not needed for initial > > rendering" is enough, possibly there are needs for more finegrained > > prioritization like "needed for initial rendering/needed for page > > feature X that is commonly used, needed for other". > > The low-priority flag should help with this. To give more control, if > RequestInit (part of Fetch) exposes HTTP2's priorities, authors can > directly manipulate that. > > > > [Use-case P:] download dynamic page components (e.g. maps) only on > > larger devices. > > Long term, we could add a media="" attribute to <script> to make this > easier. Short term, you can do it with scripts by checking the width of > the device and calling load() on the script if you want it. > > If you're a browser vendor who wants to implement <script media>, please > comment on this bug: > > https://www.w3.org/Bugs/Public/show_bug.cgi?id=23509 > > > > [Use-case Q:] I am dynamically loading one of those social widgets that, > > upon load, automatically scans a page and renders social buttons. I need > > to be able to preload that script so it's ready to execute, but decide > > when I want it to run against the page. I don't want to wait for true > > on-demand loading, like when my user clicks a button, because of the > > loading delay that will be visible to the user, so I want to pre-load > > that script and have it waiting, ready at a moment's notice to say "it's > > ok to execute, do it now! now! now!". > > You'd declare it like this: > > <script ... load-policy="when-needed precache" id=w> > > ...and run it like this: > > document.scripts.w.load(); > > > > [Use-case S:] One CMS plugin wants to load "A.js" and "B.js", where B > > relies on A. Both need to load in parallel (for performance), but A must > > execute before B executes. I don't control A and B, so changing them is > > not an option. This CMS plugin [wants] to wait for some > > user-interaction, such as a button click, before executing the code. We > > don't want there to be any big network-loading delay visible to the user > > between their click of the button and the running of that plugin's code. > > <script src="a.js" load-policy="when-needed precache" id=a> > <script src="b.js" load-policy="when-needed precache" needs=a id=b> > > <input type=button onclick="document.scripts.b.load()"> > > > > Another CMS plugin on this same page wants to load "A.js", "C.js", and > > "D.js". This plugin doesn't know or care that the other plugin also > > requests "A.js". It doesn't know if there is a script element in the > > page requesting it or not, and it doesn't want to looking for it. It > > just wants to ask for A as a pre-requisite to C and D. But C and D have > > no dependency on each other, only a shared dependency on A. C and D > > should be free to run ASAP (in whichever order), assuming that A has > > already run [once] some user-interaction that initiates the load of A, > > C, and D. This user interaction may be before the other plugin requested > > A, or after it requested A. > > First the previous part: > > <script src="a.js" load-policy="when-needed precache" id=a1> > <script src="b.js" load-policy="when-needed precache" needs=a1 id=b1> > <input type=button onclick="document.scripts.b1.load()"> > > Now this plugin: > > <script src="a.js" load-policy="when-needed precache" id=a2> > <script src="c.js" load-policy="opportunistic precache" needs=a2 id=c2> > <script src="d.js" load-policy="opportunistic precache" needs=a2 id=d2> > <input type=button > onclick="document.scripts.c2.load(); document.scripts.d2.load();"> > > Because the load-policy doesn't contain "force" (which would be in the > default for scripts), the second <script src="a.js"> defers to the first > one. > > > > "A.js" can be requested relatively (via a <base> tag or just relative to > > document root) or absolutely, or it might be requested with the > > leading-// protocol-relative from, taking on whatever http or https > > protocol the page has, whereas other references to it may specify the > > prototcol. > > That just works with the above, since the src="" attribute is resolved > before being compared. > > > > These plugins can't guarantee what ID's or classes they use are reliably > > unique without undue processing burden. > > Ah. This is more problematic. > > If the plugins are put into HTML imports, this works because imports have > their own ID scope. Is that sufficient? > > > > [Use-case T:] I have two different calendar widgets. I want to pop one > > of them up when a user clicks a button. The user may never click the > > button, in which case I don't want the calendar widget to have ever > > executed to render. [...] > > > > It'd be nice if both calendar widgets were built sensibly so that > > loading the code didn't automatically render. One of them IS, the other > > is unfortunately mis-behaving, and it will render itself as soon as its > > code is run. [...] > > > > Furthermore, these two widgets are not "equal". Perhaps one is better > > for smaller browser window sizes, and the other is better for larger > > browser windows. [...] > > > > Regardless, the point is, there's run-time conditions which are going to > > determine if I want to execute calendar widget A or B, or maybe I never > > execute either. But I want them both preloaded and ready to go, just in > > case, so that if the user DOES need one, it's free and ready to execute > > with nearly no delay, instead of having to wait to request as I would > > with only on-demand techniques. > > <script src="calendar1.js" load-policy="when-needed precache" id=cal1> > </script> > <script src="calendar2.js" load-policy="when-needed precache" id=cal2> > </script> > > <script> > function showCalendar() { > if (useCalendar1) > document.scripts.cal1.load(); > else > document.scripts.cal2.load(); > } > </script> > > <input type=button onclick="showCalendar()"> > > > > [Use-case U:] I have a set of script "A.js", "B.js", and "C.js". B > > relies on A, and C relies on B. So they need to execute strictly in that > > order. [Now], imagine they progressively render different parts of a > > widget. [...] I only want to execute A, B and C once all 3 are preloaded > > and ready to go. It's [...] about minimizing delays between them, for > > performance PERCEPTION. > > > > [For example, one of them might start playing a video, and another might > > introduce the <canvas> slides for that video. You want all of the > > relevant scripts to be run at once, so there's no point where the page > > has a <video> element but doesn't have the <canvas>.] > > <script src="A.js" id=A load-policy="late-run"></script> > <script src="B.js" id=B load-policy="late-run" needs=A></script> > <script src="C.js" id=C load-policy="async" needs=B></script> > > > > [Use-case V:] you have a string of scripts ("A.js", "B.js", and "C.js") > > loading which constitute a dependency chain. A must run before B, which > > must run before C. However, if you detect an error in loading, you stop > > the rest of the executions (and preferably loading too!), since clearly > > dependencies will fail for further scripts, and the errors will just > > unnecessarily clutter the developer console log making it harder to > > debug. > > That's the built-in behaviour. > > > > [Use-case W:] some developers have even requested to be able to stop the > > chain and prevent further executions if the script loads, but there's > > some compile-time syntax error or run-time error that happens during the > > execution. For them, it's not enough for B to simply finish loading > > successfully, but that it must fully execute without error. > > If something throws, it'll trigger the same failure behaviour as failing > to load at all. > > > > [Use-case X:] not all dependencies are JS files, e.g. authors use > > plugins to load template files, JSON, images, etc. > > Images are handled since you can use an <img> element as the target of a > needs="" attribute. You can add dependencies on manually-started loads > using promises; these won't automatically trigger the loads, but they > will cause the loads to wait. > > > > [Use-case Y:] not all dependencies are usefully satisfied immediately > > after their JS file is loaded, e.g. some libraries may need asynchronous > > initialization. > > You can add a promise as a dependency. > > > > [Use-case Z:] Another common kind of dependency scripts have is presence > > of certain element in the DOM, e.g. `dropdown-menu.js` may require `<nav > > id="menu">` to be in the document _and_ have its content fully parsed > > before the script can run. > > needs="" can point to any element by ID; it'll only be resolved once the > ID is present in the DOM. > > > On Tue, 27 Aug 2013, Ian Hickson wrote: > > > > Jake also mentioned these requirements: > > > > | - Provides an adoption path for browsers that don't support the new > > | feature (happy for the fallback to be blocking document-order > > | execution) > > That's the fallback for the declarative features. (This might actually > be a problem for load-policy=declare.) > > > > | - Is discoverable by pre-parsers (so async=false and old-IE's > > | readystate methods aren't enough) > > This is true for the declarative cases, at least. Note though that if > we make everything async (as this essentially would), preparsing > becomes much less important. > > > > And Kyle mentioned this scenario that we need to handle as well (not > > strictly a use case, more a variant on the above use cases): > > > > > I want to preload a script which is hosted somewhere that I don't > > > control caching headers, and to my dismay, I discover that they are > > > serving the script with incorrect/busted/missing caching headers. > > We can handle this by defining the behaviour of "precache" more > explicitly, or putting options or load-settings="", but I'm not sure > what behaviour we want exactly. What headers should we ignore? When? > > > On Wed, 23 Jul 2014, Ben Maurer wrote: > > On Tue, Jul 22, 2014 at 5:33 PM, Ian Hickson <ian@hixie.ch> wrote: > > > > > > Why not: > > > > > > var mystyle = E('link', { rel: 'stylesheet', href: 'my.css', > > > whenneeded: true }); > > > document.body.appendChild(mystyle); > > > var myfetch = mystyle.fetch; > > > ... > > > > Yeah, I think that API would be perfect (assuming that whenneeded=true > > initiates the fetch, but not the load into the document). In combination > > with allowing the user to specify an attribute with a set of parameters > > to the fetch algorithm (eg, a custom header) I think that would cover > > the use cases I was thinking of. > > So this would be: > > var mystyle = E('link', > { rel: 'stylesheet', href: 'my.css', > 'load-policy': 'declare prefetch', > // declare prevents it from ever being applied > 'load-settings': '...' > // this is your custom headers, etc > }); > document.body.appendChild(mystyle); > mystyle.load(); // triggers the fetch > // since it's marked "declare", it doesn't get automatically applied > mystyle.loaded.then(function () { > // we can remove the 'declare' part of the policy to make it apply: > mystyle.loadPolicy.remove('declare'); > // alternatively we could create a new <link rel=stylesheet> element > // and insert that; since the URL is the same it would reuse what it > // got for mystyle instead of starting anew. > }); > var myfetch = mystyle.request; > // you can manipulate the priority or whatever with myfetch > > -- > Ian Hickson U+1047E )\._.,--....,'``. fL > http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,. > Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Received on Monday, 25 August 2014 20:54:27 UTC