walmartlabs / little-loader

A lightweight, IE8+ JavaScript loader
MIT License
370 stars 28 forks source link

Support loading multiple JavaScript files before calling back #39

Open coopy opened 8 years ago

coopy commented 8 years ago

I'm considering using little-loader to dynamically load in component suites at run-time. Currently, there is only support for a singular src string. I would welcome support for an array of src strings, to load in multiple libraries. Is this in alignment with the roadmap?

ryan-roemer commented 8 years ago

@coopy -- We're aiming to keep little-loader razor thin. I think this is most appropriate for documentation / best practices around "patterns" captured in: https://github.com/walmartlabs/little-loader/issues/20

Extra pattern suggestions and documentation PRs most welcome!

Also, I'm open to examples (maybe in the test suite) we can add to.

For you here, here's a strawman pattern for parallel loading:

var load = window._lload; // or however required in...

var parallelLoad = function (libs, callback) {
  var finished = 0;
  var errs = [];

  if (!(libs && libs.length)) { return callback([]); }

  libs.forEach(function (lib) {
    load(lib, function (err) {
      // capture errors array and return all to callback.
      // Note: _Could_ do other logic to bail early when first error occurs.
      errs = errs.concat(err ? [err] : []); 

      finished++;
      if (finished === libs.length) {
        callback(errs);
      }
    });
  });
};

parallelLoad([
  "foo.js",
  "bar.js"
], function (errs) {
  console.log("ALL DONE!");
  console.log("Errors: " + errs);
});

And, here's a strawman pattern for sequential loading:

var load = window._lload; // or however required in...

var seriesLoad = function (libs, callback) {
  // Load function and recursively call next function.
  var loadLib = function (idx) {
    var lib = libs[idx];

    // Done with all libs.
    if (typeof lib === "undefined") {
      callback();
    }

    // Load this lib.
    load(lib, function (err) {
      // Short circuit if error.
      if (err) { return callback(err); }

      // Recursively go for next.
      loadLib(idx + 1);
    });
  }

  // Start at first lib index.
  loadLib(0);
};

seriesLoad([
  "foo.js",
  "bar.js"
], function (err) {
  console.log("ALL DONE!");
  console.log("Error: " + err);
});
exogen commented 8 years ago

I'm not against adding a helper for this at all, but the problem is that it muddies the waters a bit w.r.t. the main important feature of little-loader's callback: that it executes synchronously following the script's execution. Touting this benefit while allowing multiple scripts to be passed might make people think that all the loaded scripts are executed in one synchronous block along with the callback, when in reality other code would be allowed to run in between each script's execution.

I'm assuming you're just using little-loader because it's a nice & small script loader, and don't need our strong callback guarantees for the case of multiple scripts?

ryan-roemer commented 8 years ago

@coopy @exogen -- I've updated by two patterns above. As you can see with even these simple scenarios, there is a lot of choice about how to handle errors / early termination / etc.

I am definitely up for documenting / explaining these patterns.

I'm potentially open to including them as separate includes as utilities...

jknight12882 commented 8 years ago

What would be particularly great is if it were possible to download several files in parallel, but execute them in order. This does not seem possible with the current library, but would greatly help bootstrapping applications that have several external dependencies

exogen commented 8 years ago

@jknight12882: That's unfortunately not possible due to CORS – it's one reason why AMD was invented! :)

jknight12882 commented 8 years ago

@exogen CORS has nothing to do with it. CORS deals with making ajax calls. You can load <scripts> from any domain. (see jsonp).

jknight12882 commented 8 years ago

You could control evaluation order by not appending the script tags to the DOM until all the scripts have downloaded

exogen commented 8 years ago

@jknight12882: It does have to do with CORS, trust me. You can't get the content of scripts from other domains, which would be required for delaying their execution.

jknight12882 commented 8 years ago

@exogen see my comment above about controlling evaluation order by waiting til all scripts have downloaded before appending them to the DOM.

exogen commented 8 years ago

You could control evaluation order by not appending the script tags to the DOM until all the scripts have downloaded

Try it and report back my friend – this is a well understood topic! :)

jknight12882 commented 8 years ago

my mistake, it looks like the delayed appending to DOM was only for < IE10.

exogen commented 8 years ago

@jknight12882: True that, readyState would make it possible for old IE, but other browsers only have load, which is fired after execution. That's why you'd need AJAX + eval to do it on other browsers, but then, CORS.