- From: Ryosuke Niwa <rniwa@apple.com>
- Date: Tue, 03 Sep 2013 14:49:29 -0700
- To: Ian Hickson <ian@hixie.ch>
- Cc: "whatwg@whatwg.org" <whatwg@whatwg.org>
Original proposal: http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-August/040664.html http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-August/040666.html In order to address use cases incDependencies and decDependencies satisfied, I'm going to add the following proposals: I make one more change that 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. 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. On Aug 27, 2013, at 2:55 PM, Ian Hickson <ian@hixie.ch> wrote: > > First, let's get down to use cases. Kyle did a great job of describing > some key use cases: > > 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!". <script id="social" src="social-button.js" prefetch></script> <button onmouseover="document.scripts.social.execute()"> ... </button> >> [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. // CMS plugin 1 var A = E('script', { src: 'A.js', prefetch: true }); var B = E('script', { src: 'B.js', needs: 'A.js', prefetch: true }); document.body.append(A, B); function sawUserInteraction() { B.execute(); }; >> 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. // CMS plugin 2 var A = E('script', { src: 'A.js', prefetch: true }); var C = E('script', { src: 'C.js', needs: 'A.js', prefetch: true }); var D = E('script', { src: 'D.js', needs: 'A.js', prefetch: true }); document.body.append(A, C, D); function sawUserInteraction() { C.execute(); D.execute(); }; >> "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. [...] <script id=calA src="a.js" prefetch></script> <script id=calB src="b.js" prefetch></script> <script> function showCalendar(which) { if (which == 'a') document.scripts.calA.execute(); else document.scripts.calB.execute(); } </script> Neither Ian's nor my proposal fallbacks gracefully here :( >> [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" prefetch></script> <script src="B.js" prefetch needs="A.js"></script> <script id=c src="C.js" prefetch needs="B.js"></script> <script> // when we need it... document.scripts.c.execute(); </script> > 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. <script src="A.js" prefetch></script> <script src="B.js" prefetch needs="A.js"></script> <script id=c src="C.js" prefetch needs="B.js"></script> <script> onerror = function (message, source, lineno, colno, error) { // report error } // when we need it... document.scripts.c.execute(); </script> >> [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. <script id=a src="A.js" whenneeded></script> <script src="B.js" whenneeded needs="A.js"></script> <script id=c src="C.js" whenneeded needs="B.js"></script> <script> function onAfailedToInitialize() { document.scripts.a.src = null; } // when we need it... document.scripts.c.execute(); </script> > 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. <script src="deps.js" async></script> <script src="build.js" needs="deps.js"></script> // in deps.js: var me = document.currentScript; var image = new Image(); image.src = 'image.png'; currentScript.needs.add(image); >> [Use-case Y:] not all dependencies are usefully satisfied immediately >> after their JS file is loaded, e.g. some libraries may need asynchronous >> initialization. I don't intend to address use case but we could make it so that script element without src is an indefinite dependency as in: <script src="slowLoad.js" async></script> <script src="next.js" needs="slowLoad.js"></script> // in slowLoad.js: var me = document.currentScript; var dummyScript = document.createElement('script'); me.needs.add(dummyScript); me.incDependencies(); setTimeout(init, 1000); function init() { // ok, we're ready me.needs.remove(dummyScript); } >> [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. I guess we can somehow make needs.add take an id as well as a script name and a node? Or add a method like requireElementWithId as in: <script src="navi.js" async></script> // in nav.js: document.currentScript.requireElementWithId('menu'); - R. Niwa
Received on Tuesday, 3 September 2013 21:50:07 UTC