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].
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?
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...
> 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).
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 :).
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?
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.