Hacker News new | past | comments | ask | show | jobs | submit login
LoadJS – A tiny async loader for modern browsers (710 bytes) (github.com/muicss)
91 points by andres on Dec 14, 2016 | hide | past | favorite | 24 comments



https://github.com/walmartlabs/little-loader is smaller (519 bytes min+gzipped), supports IE8, and fires either on script load or on error synchronoulsy (so that globals won't be overwritten by code that runs between script loading and the callback firing). The thing is tested on SauceLabs down to IE8.

The drawback is a lower level API, and it is JS only. AFAIK loading CSS consistently cross browser is also a mine field if you want consistent error handling.

    window._lload("http://example.com/foo.js", function (err) {
        // `err` is script load error.
        // otherwise, foo.js is loaded!
      }/*, [optional context (`this`) variable here] */);


The original bare-bones version of LoadJS was down to 455 bytes and grew to 710 as we added some useful features (e.g. CSS loading, async false support, load retries).

Initially I was hesitant to add CSS support because handling load errors across browsers is very tricky but I think we came up with a good solution [1] that is tested down to IE9 [2].

[1] https://github.com/muicss/loadjs/blob/master/src/loadjs.js#L...

[2] https://github.com/muicss/loadjs/blob/master/test/tests.js#L...


Good job on detecting CSS loading failures.

The little-loader also has a section to detect JS loading and errors in IE8/9 (with a dedicated sub-section for the former), I didn't see anything like this in LoadJS... Did you find a terser workaround?

https://github.com/walmartlabs/little-loader/blob/42d233f360...

I'm somewhat familiar with little-loader because I've worked at making it smaller, but I'm not competent to judge whether its approach is necessarily the best...


We use the `onerror` handler to detect load errors on IE9: https://github.com/muicss/loadjs/blob/master/src/loadjs.js#L...


This looks like the perfect case for using Promises...

    loadjs('foobar').then(() => {
      // foobar.js loaded
    });
And then with the newer spec:

    await loadjs('foobar');
    // foobar.js loaded
Such a pity that instead it uses a proprietary format.

Edit: you could still add all other nice things without compromising the main thing, for example to add a "before":

    loadjs.before(fnA).before(fnB).load('foobar').then(() => {});


> This looks like the perfect case for using Promises...

I think promises are a bad idea if loading JavaScript is on the critical path to rendering your page. Unless you have so much JavaScript that parsing takes a long time and you need to yield to the layout and composition engine...

I wrote my own bundle loader so that:

* cached JavaScript loads fast

* CSS is put into a JavaScript string and loaded as a JavaScript file so (1) success and error handling code is simplified and (2) because error handling for CSS is hard -- e.g mycss.js looks like insertCssStyle('.much {css: here}');

* Small images are compiled into the CSS as data. Fonts are loaded in the CSS as data (avoiding a couple of bugs).

* it starts loading in <head> (avoiding much delay).

* It inserts <script> elements (rather than use XHR and eval) so that stack traces are readable, and cross origin works well, and so the browser can multithread the loading and parsing.

* it requests scripts from a single cross origin cloudflare host (for speed and caching), but re-requests from the page's domain if that isn't working (for reliability).

* loading failures are logged to third party (for checking reliability).

* JavaScript bundles are wrapped in try/catch for logging, and they explicitly call a function too indicate the bundle loaded successfully (and to chain dependencies correctly for async loading).

It is hard to make a single page app load reliably, and to report failures correctly if it fails!


Promises are actually bad for that use case because you want the completion callback to run synchronously on load, otherwise, foreign code may trample a global between the time the script loads and the time the 'loaded' function runs.

This may not be an issue with native ES6 Promises (since they rely on microtasks) but other implementations/polyfills may not be airtight (setTimeout-based ones aren't for sure, for example).


Someone please correct me if I’m wrong, but I’m pretty sure await has to be within an async function [0].

[0] https://github.com/tc39/ecmascript-asyncawait/issues/36


Yeah but we can use the same "trick" we've been using for years, an IIFE:

    (async () => { 
      // Code...
    })()


This will wait within the function, but won't block the caller.


which is what it should do

If you want the caller to "block", then it needs to await or .then() what it's calling.


Checkout the import() proposal:

https://github.com/tc39/proposal-dynamic-import

It basicly is what you are describing.


Also, it's a spec in the making `System.import('foobar')` only that actually works with modules rather than scripts.


I made a similar thing that is supposed to be vaguely require.js compatible in 541-588 bytes, depending on enabled features [1]. Not production ready :).

[1] https://github.com/gliptic/hypjs/blob/master/src/amd.ts


https://github.com/QubitProducts/micro-amd is another project with similar goals. Its pretty small, amd compliant and battle tested


Seems handy for the callbacks, but will this be as useful once http2 is the default?


Yes. I'm thinking about using it for conditionally rendered stuff.


back in the day you just did script src... none of this bullshit


It's not 1999 anymore


Wow. This makes Labjs, which has been my preferred loader, seem heavy.


That's cool. Would this help with my page speed score that almost is always like a D- for stuff related to async/css/js and 'deferred' loading?


Maybe, but you can make it better even without any fancy loaders, async and defer are regular attributes of the script tag – https://developer.mozilla.org/en/docs/Web/HTML/Element/scrip...


`defer` has problems: http://stackoverflow.com/a/40535440/247696

There is no obvious easy way to wait for an `async` script to finish loading and executing before running code that depends on it. For instance, how would I load the jQuery library asynchronously before running code that depends on jQuery?


It seems to lack the handling of timeouts, which is necessary in these async days.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: