/paraload

asset loader for parallel loading yet ordered execution

Primary LanguageJavaScript

paraload

paraload is an asset loader (~1.9kb minified and gzipped) for parallel loading yet ordered execution of <script>s and <link>s with respect to their dependencies. it ships with a minimal Promisese A+ v1.1 compliant library whif, thus exposes two globals: whif and paraload (which you may both rename via .noConflict() hook).

deprecation notice: this library lost its purpose to the new HTTP/2 protocol. The loading strategy should be implemented on the server-side using the Server Push, effectively loading multiple resources in response to a single request.

usage

paraload loads all resources in parallel yet executes them in the order given by your dependency tree. the <xml> node, one root node within it (no root siblings), and the <script> below are required. tag names within <xml> can be random. separate urls by line terminators. in this typical example jquery and underscore will be executed as soon as they arrive. backbone will start loading right away just like the others but will only be executed after the parent level's resources have been executed.

...
<head>
  ...
  <xml style="display:none"><r>
    //ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js
    //cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js
    <r>
      //cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js
    </r>
  </r></xml>
  <script src="path/to/dist/paraload.min.js"></script>
  ...
</head>
...

public api

paraload exposes two static functions which both return promises.

paraload
  .load('url/to/script.js') // resolves with the url
  .then(paraload.exec)      // returns another promise
  .then(function(url){
    // dependent code
  })
  .then(null, function(reason){
    // roadmap: timeout
  });

var rename = paraload.noConflict();

how it works

load without execution

a script is loaded via an <img> tag with its src attribute set. when loaded, the element's error event fires, the script gets inserted into the DOM which will fetch the already loaded file from the browser's cache and execute it immediately. this process is wrapped in a promise which, when resolved, executes the next lower level's scripts as soon as these are loaded.

Firefox however, uses a separate cache for <img>s, which leads to the script being loaded twice. other implementations like ControlJS or headjs wait until the <body> tag is rendered and then insert <object> tags using their data attribute to set the url. paraload however, uses document.implementation.createDocument to create an internal, non-active document to adoptNode once inserted <script> tags which won't be executed when loaded but fire their error event just like the <img> tag did.

why <xml>?

IE < 10 still parses XML data islands which is an abandoned feature once used to simply serve xml data within HTML pages e.g. for reuse within select fields. those browsers will thus parse the XML and not render it onto the screen while it's still accessible to javascript. the names given to the tags can thus be given at random and chosen as short as possible. if the <xml> tag was different IE would render its textContent but ignore the tags completely (unless you inlined document.createElement('special-tagname') beforehand).

all other browsers render unknown tags as default DOM elements. that's why we have to add the inline style="display:none" (or class="hide" from your already loaded css in case you don't mind the FOUC, though you should). their parsed HTML is of course equally traversable like IE's XML.

tests & docs

prerequisites

  • python pygments for generating the annotated source
  • grunt-cli for use of grunt build commands
  • gzip for further compression of minified version

setup

$ git clone <this-repo> <target-folder>
$ cd path/to/<target-folder>
$ npm install

build

  • generates the annotated source to the ./docs folder
  • uglifys source to ./dist/paraload.min.js for production environments (~4kb)
$ grunt build

for convenience there is a ready-made gzip command to further compress the minified version to ./dist/promise.min.js.gz (~1.9kb (requires gzip))

$ npm run-script gzip

run tests in your browser

(requires grunt build)

$ python -m SimpleHTTPServer

then fire up your favorite browser and point it to localhost:8000/tests to run the tests or localhost:8000/docs to read paraload's story - the annotated source.

licence

MIT