kubetail-org / loadjs

A tiny async loader / dependency manager for modern browsers (899 bytes)
MIT License
2.58k stars 150 forks source link

parallel loading order #20

Closed neuropass closed 8 years ago

neuropass commented 8 years ago

First of I love this! I'm coming from headJS which is a fantastic loader (core only) almost as little as loadJS.

Second, Looks like when you load in parallel the order changes in reverse.

For example I would logically load my files like this in parallel:

loadjs(['//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js', '//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js'], 'thunk');

however in the dom the files are changed in reverse and the output is this:

loadjs(['//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js', '//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js'], 'thunk');

in this case seeing as bootsrtap needs jQuery first the changes that the scripts will conflict are very high.

is this a bug or is intended to load the scripts in reverse from the order originally assigned?

For example headJS does this very well and respects the original order, as it loads in sequence and parallel.

Let me know your thoughts.

amorey commented 8 years ago

Great! I'm happy to hear you like loadjs!

Loadjs is meant to be used only as an async loader so it assumes that the order of the scripts doesn't matter. Internally it iterates through the scripts backwards since that's the fastest, most space efficient way to write a loop: https://github.com/muicss/loadjs/blob/master/src/loadjs.js#L126

It sounds like you're looking for parallel loading with sequential execution. Can you use <script> tags in the <head> to implement it?

neuropass commented 8 years ago

Hi, Yes I would like to use parallel loading with sequential execution so the scripts will not fail. :)

I have loadJS initiated here

<script src="/vendors/loadjs.min.js"></script> 
</head>
<body>

and then all my scripts loaded before the

</body>

like this for example: loadjs(['//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js', '//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js'], 'thunk');

and then some extra to load bits and pieces once all the scripts above are ready:

loadjs.ready(['thunk', 'frmfunc'],
function() {
  // foo.js & bar.js & thunkor.js & thunky.js loaded
    $('a').tooltip({trigger : 'hover',container: 'body'})   
},
function(depsNotFound) {
    if (depsNotFound.indexOf('thunk') > -1) {};  // foo failed
   // if (depsNotFound.indexOf('boostr') > -1) {};  // bar failed
    if (depsNotFound.indexOf('frmfunc') > -1) {};  // thunk failed
});

What should I do to have it working in parallel but respect the sequence?

Thank-you so much again!

amorey commented 8 years ago

This simplest way to fetch files in parallel and execute them in sequence is to use <script> tags in the HTML. For example this will fetch script1.js and script2.js in parallel and execute them in series:

<html>
  <head>
    <script src="/path/to/script1.js"></script>
    <script src="/path/to/script2.js"></script>
  </head>
  <body>
  </body>
</html>

Async loading libraries like loadjs are useful for fetching files after the initial DOM has been rendered to improve performance. For example, you could use loadjs to load Google Analytics after your page has loaded:

<html>
  <head>
    <script src="/path/to/script1.js"></script>
    <script src="/path/to/script2.js"></script>
    <script src="/path/to/loadjs.js"></script>
    <script>
      window.addEventListener('load', function() {
        loadjs(['/path/to/google-analytics.js']);
      });
    </script>
  </head>
  <body>
  </body>
</html>
neuropass commented 8 years ago

The thing is, wouldn't be faster if all is handled by loadjs to take care of all the scripts? also becuase for example you cant really add some scripts before and then some other modules to be loaded with loadjs, because they will be pushed before and therefore certain scripts will still fail... That's why I think loading in parallel but with sequential order should be respected in the loader so you can load all your scripts asych and have consistent result.

amorey commented 8 years ago

In an ideal world it would be great to use an async loader (e.g. loadjs) to fetch scripts in parallel and execute them in series. Unfortunately there many edge cases and currently it's beyond the scope of this particular library. I'd recommend reading this article for a deep dive into script loading: http://www.html5rocks.com/en/tutorials/speed/script-loading/

Given all the edge cases, the simplest thing to do is to use <script> tags in the <head> when you want to fetch files in parallel and execute them in series, and loadjs to load scripts asynchronously.

What are you trying to do in particular? There are a lot of things you can do with loadjs.done() and loadjs.ready() to keep your script loading fast and simple.

amorey commented 8 years ago

@neuropass I added support for fetching files in parallel and executing them in series via an async: false option:

loadjs(['/path/to/file1.js', '/path/to/file2.js'], {
  success: function() { /* files loaded successfully */ },
  async: false
});

The code is in the loadjs:asyncfalse branch: https://github.com/muicss/loadjs/tree/asyncfalse

Please try this file and let me know if it works for you: https://raw.githubusercontent.com/muicss/loadjs/asyncfalse/dist/loadjs.min.js

amorey commented 8 years ago

Support for fetching files in parallel and executing them in series is implemented in v2.0.0: https://github.com/muicss/loadjs

Let me know if you run into any issues using the feature.