Last active
December 17, 2015 13:39
-
-
Save jakearchibald/5618497 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
In an HTTP2 world, delivering multiple small individually cachable | |
and executable scripts can be more efficient than one big | |
concatenated script. Can we better prepare for this? Can it be | |
done in a way that works with existing JS files? | |
Imagine a page that had 4 interactive modules, all of which | |
depended on a couple of utility scripts. We could progressively | |
add behaviour to the page by executing the 'action' scripts | |
as soon as they download & the utilities have executed. | |
This means the execution of actions4.js isn't blocked on | |
actions1-3. | |
We still want scripts defined in HTML, so preloaders and scanners | |
pick them up and start the download as soon as possible, without | |
executing JavaScript. | |
--> | |
<!-- | |
In this example, we introduce a 'future' attribute. | |
'future' downloads the scripts as 'async', but does not execute | |
them. Browsers that don't support 'future' will fall back to | |
synchronous in-order execution. | |
--> | |
<script src="utils1.js" class="script-utils-1" future></script> | |
<script src="utils2.js" class="script-utils-2" future></script> | |
<script src="actions1.js" class="script-actions" future></script> | |
<script src="actions2.js" class="script-actions" future></script> | |
<script src="actions3.js" class="script-actions" future></script> | |
<script src="actions4.js" class="script-actions" future></script> | |
<script> | |
if (document.scripts[0].execute) { // simple feature detect | |
// execute() returns a Future which resolves on successful | |
// parse & execution (no errors thrown in initial execution) | |
document.querySelector('.script-utils-1').execute().then(function() { | |
// utils2 depends on utils1, so execute it now | |
return document.querySelector('.script-utils-2').execute(); | |
}).then(function() { | |
// the rest of the scripts depend on utils1 & utils2 | |
// but can run execute in any order | |
return Future.every( | |
toArray(document.querySelector('.script-actions')).map(function(script) { | |
return script.execute(); | |
}); | |
); | |
}).then(function() { | |
// all scripts have executed | |
}); | |
} | |
</script> | |
<!-- | |
This example does the same as above, but entirely declaratively. | |
'depends' downloads the scripts as 'async', but does not execute | |
until all scripts matching the selector(s) have executed. | |
Empty 'depends' will behave exactly as 'async'. | |
Browsers that don't support 'depends' will fall back to | |
synchronous in-order execution. | |
However, this syntax allows for circular dependencies. Also, | |
what happens if I dynamically add a script.script-utils before | |
one of the action scripts has finished downloading? | |
What if I had a img[src=utils1.js], or even div[src=utils1.js] | |
in the document by accident? | |
--> | |
<script src="utils1.js" class="script-utils" depends></script> | |
<script src="utils2.js" class="script-utils" depends="[src=utils1.js]"></script> | |
<script src="actions1.js" class="script-actions" depends=".script-utils"></script> | |
<script src="actions2.js" class="script-actions" depends=".script-utils"></script> | |
<script src="actions3.js" class="script-actions" depends=".script-utils"></script> | |
<script src="actions4.js" class="script-actions" depends=".script-utils"></script> | |
<script depends=".script-actions"> | |
// all scripts have executed | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Circular dependencies are really easy to detect. It is harder how to handle them.
Some options that I can think of:
I favour failures with errors over trying to do stuff with warnings, but the latter has been the web way of working since the first browsers. In any case, the possibility to screw up is - in my opinion - not a valid reason for not doing it, if it is so easy to get it right.