Frage

I made a very simple example (adapted from my real project) that uses LABjs (v2.0.3) to load the javascript files and execute them in a given order. I'm pasting the code below.

  • Since testLAB.js waits for mainCanvas.js, who waits for events.js, I expect the order of the alerts to be: "events.js" "mainCanvas.js" "testLAB.js".
  • However, I get usually either the reversed order: "testLAB.js" "mainCanvas.js" "events.js".
  • Sometimes I get "testLAB.js" "events.js" "mainCanvas.js".

Can anybody explain?
The full example can be downloaded here.

EDIT: I'm using node.js and the http-server module to serve these pages locally (in case you want to try it out locally as well)

file: index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Test lab.js</title>
    <script src="js/lib/LAB.js"></script>
    <script src="js/app/testLAB.js"></script>
</head>
<body>
</body>
</html>

file: js/app/testLAB.js

$LAB.setGlobalDefaults({
    BasePath: "/js/",
    Debug:true
});

$LAB
.script("lib/underscore.min.js")
.script("app/mainCanvas.js").wait(function(){
    alert("testLAB.js");
});

file: js/app/mainCanvas.js

$LAB
.script("lib/jquery.js").wait()
.script("lib/underscore.min.js")
.script("app/events.js")
.wait(function() {
    alert("mainCanvas.js");
});

file: js/app/events.js

$LAB
.script("lib/jquery.js")
.script("lib/underscore.min.js")
.wait(function() {
alert("events.js");
});
War es hilfreich?

Lösung

The problem here is a misunderstanding of how LABjs works, or rather, of how nested dynamic script loading works.

If I load script A.js using a script loader, the browser will download and then execute the contents of that script, and inside the script A, I use the script loader to load script B.js, the fact that script A.js asked for B.js to load does not mean that A.js's "load" event is magically blocked until the dependencies (that is, in this case, B.js) finish loading and executing.

Once a script has loaded, it's executed immediately by the browser, and once its main execution finishes, the browser will fire the "load" event on that script signaling it's done. If that script fired off some other asynchronous behavior, such as loading more scripts, that asynchronous behavior will have no effect on the main execution thread or the firing of that script's "load" event.

If you need to load nested dependencies, you will need a more sophisticated system of "deferring" each file's execution, similar to how AMD loaders like Require.js wrap all code in functions. The reason that technique works is that the execution order of the outer function being declared is irrelevant, because then after all code is defined, you can come back and execute those function payloads in their various correct orders and get the sequence you expect.


The other (simpler?) option, which is what I do personally, if you don't want to go the AMD/Require route, is to use a simple build tool script that reads through your JS files at build time, discovers what all the nested dependencies are, and "hoists" all of them, in the correct order, into a single "global" $LAB chain call. So inside your files, instead of declaring nested dependencies with actual $LAB calls, just have a notation of the depdendencies, like in a JS comment, and have your script look for those.

Example (similar to how I do things myself):

A.js:

// needs "B.js"
// needs "C.js"

A.something = function( /*..*/ ) { /*..*/ };    
B.something();
C.something();

B.js:

// needs "D.js"

B.something = function( /*..*/ ) { /*..*/ };
D.something();

C.js:

// needs "D.js"
// needs "E.js"

C.something = function( /*..*/ ) { /*..*/ };
D.something();
E.something();

D.js:

D.something = function( /*..*/ ) { /*..*/ };

E.js:

E.something = function( /*..*/ ) { /*..*/ };

Then, I have a simple script which looks through my files, and discovers the necessary execution order so that all dependencies are met.

It then produces a single $LAB chain for use in my main page, that looks like this:

$LAB
.script("D.js")
.script("E.js").wait() // D and E can execute in either order
.script("B.js")
.script("C.js").wait() // B and C can execute in either order
.script("A.js")
.wait(function(){
   alert("Everything is loaded!");
});

The only caveat to an approach like I'm suggesting is that you can't do circular dependencies (like C depends on B and B depends on C). If you need circular dependencies, go AMD/Require.js.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top