title: Race condition in jquery when/done sequence?
body: <p>I have a web applciation based on apache, php, js/jquery. One module of the application is initialized when the document is loaded. That sequence calls an init() funcion that performs a number of tasks, amongst others it fetches two html dialogs via ajax and inserts them into the existing page. A reference to those dialogs is saved as variables, so that I don't have to specify jquery selectors throughout my scripts but can simply use those variables instead. This work fine, except that there are very few cases (now and then...) when the variables are 'undefined' when trying to bind a handler to an element inside the fetched dialogs. Which is odd, because apart from that bind the dialogs are usable throughout the application. This suggests that the ajax calls succeed, but aparently there is some kind of race condition, so that now and then the variables are only set <em>after</em> the bind attempt. </p>
  
  <p>In my understanding this should not happen, since the bind is done in the .done() part of a when() and should only be executed after the ajax retrieval of the dialogs has finished inside the when(). </p>
  
  <p>Aparently I am missing something fundamental here. Anyone got a hint for me ? </p>
  
  <p>The following cites code excerpts from the working implementation. They may appear syntactically invalid as some parts, but that is just due to the removal of code parts not relevant here. The unshortened code works fine. </p>
  
  <p>The variables: </p>
  
  <pre><code>Shorty.Tracking.Dialog.List:{};
  Shorty.Tracking.Dialog.Click:{};
  </code></pre>
  
  <p>The initializing sequence: </p>
  
  <pre><code>$(window).load(function(){
    //...
    var dfd = new $.Deferred();
    $.when(
      // load layout of dialog to show the list of tracked clicks
      Shorty.Tracking.init()
    ).done(function(){
      // bind actions to basic buttons
      Shorty.Tracking.Dialog.List.find('#close').on('click',function(){
        // ...
      });
      // ...
    });
    // ...
  })
  </code></pre>
  
  <p>The shortened init function: </p>
  
  <pre><code>Shorty.Tracking.init:function(){
      // ...
      var dfd=new $.Deferred();
      // two dialogs are used by this plugin
      var dialogs={
        'list':  Shorty.Tracking.Dialog.List,
        'click': Shorty.Tracking.Dialog.Click
      };
      // load dialogs from server
      $.each(['list','click'],function(i,dialog){
        if (...){
          // ...
          dfd.resolve();
        }else{
          // load dialog layout via ajax and create a freshly populated dialog
          $.ajax({
            type:     'GET',
            url:      OC.filePath('shorty-tracking','ajax','layout.php'),
            data:     { dialog: dialog},
            cache:    false,
            dataType: 'json'
          }).pipe(
            function(response){return Shorty.Ajax.eval(response)},
            function(response){return Shorty.Ajax.fail(response)}
          ).done(function(response){
            // create a fresh dialog
            // insert it alongside the existing dialogs in the top controls bar
            $('#controls').append(response.layout);
            switch (dialog){
              case 'list':
                Shorty.Tracking.Dialog.List=$('#controls #shorty-tracking-list-dialog').first();
                break;
              case 'click':
                Shorty.Tracking.Dialog.Click=$('#controls #shorty-tracking-click-dialog').first();
            } // switch
            dfd.resolve();
          }).fail(dfd.reject)
        } // else
      }); // each
      return dfd.promise();
    },
  </code></pre>
  
  <hr>
  
  <p>With the answer of Bergi I managed to apparently remove the original problem. Until now I could not detect any more failed bind attempts. However I could not follow that suggestion completely: in his answer he suggested to remove the switch statement in the initialization method in favor of a direct assignment. This certainly is much more elegant, but that won't work. I have the impression there is some missunderstanding on my side about how javascript handles references and/or functions stored in variables. </p>
  
  <p>Maybe you, Bergi, or someone else can shed some explaining light on this: </p>
  
  <p>This is the modifed initialization method from above: </p>
  
  <pre><code>  init:function(){
      if (Shorty.Debug) Shorty.Debug.log("initializing tracking list");
      // check if dialogs already exist
      if (   $.isEmptyObject(Shorty.Tracking.Dialog.List)
          &amp;&amp; $.isEmptyObject(Shorty.Tracking.Dialog.Click) ){
        // two dialogs are used by this plugin
        var dialogs={
          'list':  Shorty.Tracking.Dialog.List,
          'click': Shorty.Tracking.Dialog.Click
        };
        // load dialogs from server
        var dfds=$.map(dialogs,function(obj,dialog){
          // load dialog layout via ajax and append it to the collection of dialogs in the controls
          return $.ajax({
            type:     'GET',
            url:      OC.filePath('shorty-tracking','ajax','layout.php'),
            data:     { dialog: dialog},
            cache:    false,
            dataType: 'json'
          }).pipe(
            function(response){return Shorty.Ajax.eval(response)},
            function(response){return Shorty.Ajax.fail(response)}
          ).done(function(response){
            // create a fresh dialog and insert it alongside the existing dialogs in the top controls bar
            $('#controls').append(response.layout);
  //           obj=$('#controls #shorty-tracking-'+dialog+'-dialog').first();
            switch(dialog){
              case 'list':
                Shorty.Tracking.Dialog.List=$('#controls #shorty-tracking-list-dialog').first();
                break;
              case 'click':
                Shorty.Tracking.Dialog.Click=$('#controls #shorty-tracking-click-dialog').first();
                break;
            } // switch
          })
        }) // map
        return $.when.apply(null, dfds);
      }else{
        // dialogs already loaded, just clean them for usage
        Shorty.Tracking.Dialog.List.find('#list-of-clicks tbody tr').remove();
        new Deferred().resolve();
      } // else
    },
  </code></pre>
  
  <p>In the middle you see the assignment statement commented out and currently replaced by the switch statement below. I tried several variations, like obj.element and so on but failed. Outside the function the variables Shorty.Tracking.Dialog.List and Shorty.Tracking.-Dialog.Click remain empty. </p>
  
  <p>I am an absolute newbie to web stuff ans js/jquery. but I am certainly curious to learn more about this way of handling references. </p>