This is a quickie how-to post to help address a couple little gotchas you may run into when implementing LABjs onto your site.

Consider standard page code like this:

<script src="framework.js"></script>
<script src="myscript.js"></script>
<script>
  myscript.init();
</script>
<script>
  framework.init();
  framework.doSomething();
</script>

In this example, “myscript.init” is a function that is defined in “myscript.js”, and “framework.init” and “framework.doSomething” are both defined inside of “framework.js”.

The first pass at implementing LABjs to load these scripts may seem like this code would work correctly:

<script>
  $LAB
  .script("framework.js")
  .script("myscript.js")
  .wait(myscript.init);  // won't work!
</script>
<script>
  framework.init();  // also won't work
  framework.doSomething();  // also won't work
</script>

Why is my function reference undefined?

Firstly, the code “.wait(myscript.init)” seems like this is the correct way to tell LABjs to run “myscript.init” function after the scripts finish loading. But there’s a subtle problem: with regular <script> tags, the inline script block in the first snippet that has “myscript.init()” in it is guaranteed to not even be EVALUATED before the scripts synchronously have loaded before it. So, “myscript.init” is guaranteed to have been defined by that point, so it will work as expected.

But, in the second snippet, we pass a reference to “myscript.init” into the wait() function. But, “myscript.init” hasn’t been defined yet, because the chain for LABjs will execute immediately, as it starting to load the scripts. This means that at the time that .wait(…) first gets parsed, “myscript.init” doesn’t exist. It will be defined, later, when the scripts finish, but the reference is undefined at that point, and thus cannot be used yet.

So, the simple way to fix this problem is this:

<script>
  $LAB
  .script("framework.js")
  .script("myscript.js")
  .wait(function(){ myscript.init(); });
</script>
<script>
  framework.init();
  framework.doSomething();
</script>

So, what we pass to “wait” is not a reference to the as-of-yet-undefined “myscript.init”, but an anoynmous function that will call/execute “myscript.init()” at the proper time. Because of how functions and closures and execution bindings work in JavaScript, this syntax will wait to try to resolve/execute “myscript.init()” until that function is guaranteed to have been loaded (because “myscript.js” finished loading!).

It’s a simple but devious concept in JavaScript… the difference between a function reference “myscript.init” and an anonymous function that will call the intended function later “function(){myscript.init();}”.

OK, but I still have undefined code

For the exact same reason that “myscript.init” wasn’t yet defined when we tried to pass it to .wait(…), the code in the second inline <script> block, the calls to “framework.init()” and “framework.doSomething()”, those scripts do not yet exist (are undefined) at the moment the inline script block in question executes (which would be immediately after the first parse of the $LAB chain, but before either of its scripts have arrived).

So, we have to put THAT code into a .wait(…) as well, so that it waits to be parsed/executed until the right time.

<script>
  $LAB
  .script("framework.js")
  .script("myscript.js")
  .wait(function(){
     myscript.init();

     framework.init();
     framework.doSomething();

  });
</script>

We could do two separate .wait(…) calls, but that’s unnecessary in this example, so I just combine the two sets of inline code into one wait() function.

Think asynchronously

The key issue is the difference between immediately executing code (like in the original inline <script> tags) and the need to defer asynchronously executing code, by wrapping it in an anonymous function reference. This is because LABjs, by definition, loads scripts asynchronously, as opposed to regular <script> tags which are synchronous.

It may take a couple of tries before you wrap your brain completely around this stuff. But the basic pattern you can default to applying when replacing <script> tags with LABjs calls is this:

  1. For every <script src=”…”></script> tag you are replacing, you should have a “.script(…)” call
  2. For every <script>…/*inline code*/…</script> inline script block with code in it, we need a “.wait(function(){ … })” call to wrap around the code

Hopefully that helps clear up some confusion you may run into!

Bookmark and Share

This entry was written by getify , posted on Saturday November 28 2009at 11:11 pm , filed under JavaScript and tagged , . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

8 Responses to “LABjs: how to deal with inline code”

  • Arun Basnet says:

    Hi,

    I’ve been trying to work with LABjs and JQuery plugins simultaneously in a project.

    All other inline code (like ckeditor’s replace call, simple alerts, etc.) seem to work fine with LABjs but I still am having problems with handling jquery validate plugin using LABjs.

    The code snippet I am using is:

    $LAB
    .script("jquery.1.4.2.js")
    .block(function(){
    
    	$LAB
    	.script("jquery.validate.js")
    	.wait(function(){ // -- validate ruleset and messages which works normally with IE too
             $("#form1").validate({  // -- rules and messages     });
    
             })
    });
    

    // —
    normally, I was using the same script with the following convention:

    $(document).ready(function(){
          $("#form1").validate({
            // -- rules and
           });
     });
    

    and it was working fine with all browsers till I started using LABjs.

    I suppose I’ve made a very silly mistake there, but what can I do? The examples you have included in your website and even the LABjs documentation all show only simple inline code handling! :(

    Please help!!

    Thanks in advance,
    Arun

  • getify says:

    Try this:

    $LAB
    .script("jquery.1.4.2.js").wait()
    .script("jquery.validate.js").wait(function(){
       $(document).ready(function(){}
          $("#form1").validate({  // -- rules and messages     });
       });
    });
    
  • Arun Basnet says:

    Hi again,

    Many thanks for your response!
    I downloaded the latest release of jquery.validate (1.7, I was using 1.6 with jquery.1.4.2) and this problem went away!

    I’m in love with LABjs speeds up loading like magic!!! :D

    Also, is it ok if I avoid jquery DOM ready as much as I can? Because I assume any process that I need to do at document ready could be done at the end of the $LAB chain with a .wait(function(){}); block.

    Please suggest.

    Thanks a lot again!
    And sorry to have bothered you for jquery.validate’s incompatibility issue.
    Hope you don’t mind :)

    Thanks
    Arun

  • getify says:

    @Arun-
    Glad you got the jquery.validate issue squared away. But please also be aware that your technique of using $LAB chains nested inside of .wait/.block calls is less efficient. You should try to write it as all one big chain with .script and .wait calls interspersed. For instance:

    $LAB.script(…).wait(…).script(….).script(….).wait(…)……

    You’re doing stuff like this and it’s less efficient:

    $LAB.script(….).wait(function(){ $LAB.script(….).wait(…)…. })……

    Do you see the difference?

    Secondly, the “.block” is a deprecated name for “.wait”, so you should always use .wait, not .block. And make sure you’re using LABjs 1.0.2rc1, which is the latest stable available version.

    Also, do *not* assume that a $LAB.wait() call is equivalent to waiting for dom-ready. The dom-ready event is entirely separate from the dynamic loading of scripts, so you may have sometimes where the dom-ready happens before the scripts finish, and other times the reverse. So you should always use $(document).ready() to protect any code which relies on the presence/readiness of the DOM.

  • Arun Basnet says:

    Hi,

    Many thanks for the brief insight! I will keep it in mind.

    Also, I really think that there are a lot of novice labjs users who are confused like I’ve been. Could you please post some links to examples of LABjs used with various javascript libraries like jquery , yui etc. ?

    I had a very hard time coming across examples of labjs on the internet. :(
    Also, I thought I was doing the right thing loading dependent group of scripts (e.g., jquery.validate, jquery.form, etc.) into a single .block (I’ll use .wait now on though) that would load after the jquery library is loaded.

    Looking at your example above, I think I still am not 100% sure.

    Due to the dependency of validate and jquery form upon jquery, I assume the following would be efficient – I am assuming that validate and form will both loading in parallel after jquery.js finishes loading :

    $LAB
    .script(‘jquery.js’).wait(function(){$LAB.script(‘jquery.validate.js’).wait().script(‘jquery.form.js’)})

    But you stated above:

    $LAB
    .script(‘jquery.js’).wait().script(‘jquery.validate.js’).script(‘jquery.form.js’).wait();

    which looks much simpler than expected! So if I am assuming it wrong and the example you’ve given is how it should be, I suppose I can write documentready function at the earliest possible .wait ? i.e., after adding a .wait right behind .script(‘jquery.validate.js’) ?

    Also, once again, please could you post some links that you could be helpful and save you the hassle?

    Thanks

  • getify says:

    @Arun- unfortunately, I don’t know of any great examples out there other than this blog post and perhaps a few articles strewn about. I’m still the main person promoting how LABjs actually works. Hope eventually more others will pick up the mantle.

    To try and clarify for you: A single $LAB chain consists of a starting $LAB followed by a chain of one or more .script() calls and .wait() calls, interspersed as you need them to be. You should always try to avoid *multiple* $LAB calls, as they operate completely independently. Also, by nesting a $LAB inside a .wait(), you prevent that chain from even *loading* until the .wait() has executed. By doing a single chain with all your scripts, LABjs can load them *all* in parallel, but control their execution order based on where you have .wait() calls. That’s what .wait() is for, to ensure *execution* order.

    As for your $document.ready()… the use of $document.ready() is entirely separate from and orthagonal to the order of your scripts loading. It is only for making sure some logic doesn’t run until the dom is ready. LAB script loads *do not* affect dom readiness at all (they don’t hold it up) so there’s no connection that you should make between sequencing $LAB with document.ready.

    Instead, you should load all your scripts with LABjs and use .wait() where it’s most appropriate to run code. If that code happens to be dependent on the dom-readiness, wrap the code (inside your .wait()) with a document.ready. In some cases, your scripts may load and execute before dom-ready, and thus your logic will protected to run later when the dom is ready. In other cases, dom-ready will pass before your scripts load and execute, and using document.ready() will essentially pass through and execute immediately.

    If the code you need to wrap in $(document).ready() only cares about jquery, and not about the plugins, then do this:

    $LAB
    .script("jquery.js")
    .wait(function(){
       $(document).ready(function() {  /* do something */ });
    })
    .script("plugin1.js")
    .....
    

    If the code you have to run needs the plugin to be there, then do it this way:

    $LAB
    .script("jquery.js")
    .wait()
    .script("plugin1.js")
    .wait(function(){
       $(document).ready(function() {  /* do something */ });
    });
    

    Does that make sense more now?

  • Arun Basnet says:

    Great!
    Thank you so much! I guess I referred to quite old posts to have used .block. :D
    The one I’d referred to here :
    http://ajaxian.com/archives/labjs-simple-abstraction-for-loading-dependencies-correctly

    said quite clearly in the example:
    old=>

    new=>

    $LAB
    .script("jquery.js")
    .block(function(){
          $LAB
          .script("jquery.ui.js")
          .script("myplugin.jquery.js")
          .block(function(){
                $LAB.script("initpage.js");
          });
    });
    

    I know its wrong now.
    Thank you again

    I think I’m clear about it now.

    Thanks a lot again!

  • Kevin Hakanson says:

    I had a similar problem with multiple inline script blocks, but I was unable to combine them into one because they came from different components. After I realized the chain was instance based, I “continued” the chain by keeping the reference in a global variable, and using it in my future inline script block.

    Here’s this posts sample code, rewritten with my technique:

      var instance = $LAB
      .script("framework.js")
      .script("myscript.js")
      .wait(function(){ myscript.init(); });
    
      instance.wait(function(){
        framework.init();
        framework.doSomething();
      });
    

Leave a Reply

Consider Registering or Logging in before commenting.

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Switch to our mobile site