- From: Ryosuke Niwa <rniwa@apple.com>
- Date: Tue, 03 Sep 2013 16:36:44 -0700
- To: Ian Hickson <ian@hixie.ch>
- Cc: "whatwg@whatwg.org" <whatwg@whatwg.org>
Per IRC discussion, I misunderstood the timing at which these at which dependencies are executed. Now I agree it's desirable to have two values for when needed as proposed by Ian in the original e-mail. For other people following this thread's sake, a.js will execute immediately as soon as it's loaded in this example: <script src="a.js" whenneeded></script> <script id=b src="b.js" needs="a.js" whendeeded></script> // later document.scripts.b.execute(); whereas a.js doesn't get executed until b.js is loaded in this example: <script src="a.js" whenneeded=jit></script> <script id=b src="b.js" needs="a.js" whendeeded></script> // later document.scripts.b.execute(); - R. Niwa On Sep 3, 2013, at 2:49 PM, Ryosuke Niwa <rniwa@apple.com> wrote: > 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 23:37:08 UTC