javascript - jQuery deferred promises executing out of order? -


edit: important note using jquery 1.7.2, , no cannot changed version

i'm new promises , trying wrap head around them. i'm trying execute series of functions in order, waiting them complete before creating child views (this in backbone.js). here's code:

initialize: function () {     console.log('appview::initialized!');     var _this = this;      $.when( _this.processcookies() )         .then( _this.loadadscripts() )         .then( _this.createchildviews() ); },  processcookies: function () {     var def = $.deferred();     console.log('(1) process cookies');     return def.resolve(); },   /**  * instantiates new instances of child views.  */ createchildviews: function () {     var _this = this;     console.log('(4) creating child views'); },  loadadscripts: function () {      var _this = this,         def = $.deferred();      $.when(         _this.insertscript({             name: 'example1',             async: false,             src: '//www.example.com/script1.js',         }),         _this.insertscript({             is_mobile: is_mobile,             name: 'example2',             async: true,             src: '//example.com/script2.js'         })     )     .done(function () {         console.log('(3) scripts loaded');         def.resolve();     }); },  insertscript: function (script) {     var def = $.deferred(),         protocol = (document.location.protocol === 'https:' ? 'https:' : 'http:');      // dont script 2 on mobile.     if (script.name === 'example2' && script.is_mobile) {         console.log('skipping script');         return def.resolve();     }      var promise = $.ajax({         datatype: 'script',         cache: false,         async: script.async,         url: protocol + script.src,     });      promise.done( function () {         console.log('(2) single script loaded');         return def.resolve();     });   }, 

so, desired flow here is:

  1. when processcookies() function completed,
  2. execute loadadscripts function 2a. insertscript() fires, script 1 loads 2b. insertscript() fires, script 2 loads
  3. when both scripts finished, execute createchildviews function.

so, observing console.log() placeholders in code, expect see in console:

'(1) process cookies' '(2) single script loaded' '(2) single script loaded' '(3) scripts loaded' '(4) creating child views' 

however actually see is:

'(1) process cookies' '(3) scripts laoded' '(4) creating child views' '(2) single script loaded' '(2) single script loaded' 

what wrong promises, , why not executing in expected order?

$.when( _this.processcookies() )         .then( _this.loadadscripts() )         .then( _this.createchildviews() ); 

appear calling loadscripts() , createchildviews() immediately, instead try referencing function names @ .then(_this.loadadscripts) callbacks .

return def.resolve() returns jquery.deferred() object , not jquery promise object ; try adding .promise() after .resolve() return jquery promise object .

jquery.ajax() returns jquery promise object ; not necessary create new jquery $.deferred() object, return $.ajax()

initialize: function () {     console.log('appview::initialized!');     var _this = this;      $.when( _this.processcookies() )         // reference function name, not invoked         .then( _this.loadadscripts )         .then( _this.createchildviews ); },  processcookies: function () {     // no asynchronous operations appear here,      // no need include `$.deferred()` or `.promise()` object     // var def = $.deferred();     console.log('(1) process cookies');     // return jquery promise object, not deferred object     // return def.resolve().promise(); },   /**  * instantiates new instances of child views.  */ createchildviews: function () {     var _this = this;     console.log('(4) creating child views'); },  loadadscripts: function () {      //var _this = this,     //    def = $.deferred();      return $.when(         _this.insertscript({             name: 'example1',             async: false,             src: '//www.example.com/script1.js',         }),         _this.insertscript({             is_mobile: is_mobile,             name: 'example2',             async: true,             src: '//example.com/script2.js'         })     )     .done(function () {         console.log('(3) scripts loaded');         // def.resolve();     }); },  insertscript: function (script) {     // var def = $.deferred(),         protocol = (document.location.protocol === 'https:' ? 'https:' : 'http:');      // dont script 2 on mobile.     // if (script.name === 'example2' && script.is_mobile) {     //    console.log('skipping script');     //    return def.resolve().promise();     // }      var promise = script.name === 'example2' && script.is_mobile                    ? $.when()                    : $.ajax({                       datatype: 'script',                       cache: false,                       async: script.async,                       url: protocol + script.src,                     });      promise.done( function () {         console.log('(2) single script loaded');         // def.resolve();     });     }, 

edit: important note using jquery 1.7.2, , no cannot changed version

edit, updated

expected order may not possible using jquery version 1.7.2 , without modifying source.

appear return expected order when using jquery version 1.8+ , following update of deferred.then ; see http://blog.jquery.com/2012/08/09/jquery-1-8-released/ , http://bugs.jquery.com/ticket/11010

1.8.0

var dfd = {      initialize: function () {          console.log('appview::initialized!');          _this = dfd;            $.when(_this.processcookies())          // reference function name, not invoked          .then(_this.loadadscripts)          .then(_this.createchildviews);      },        processcookies: function () {          // no asynchronous operations appear here,           // no need include `$.deferred()` or `.promise()` object          var def = $.deferred(function (d) {              settimeout(function () {                  d.resolve('(1) process cookies')              }, math.floor(math.random() * 1000));          }).promise();          def.then(function (msg) {              console.log(msg);          });          return def.promise()      },          /**       * instantiates new instances of child views.       */      createchildviews: function () {          _this = dfd;          console.log('(4) creating child views');      },        loadadscripts: function () {          _this = dfd;          return $.when.apply(_this, [_this.insertscript({              name: 'example1',              async: false,              src: '//www.example.com/script1.js',          }),          _this.insertscript({              is_mobile: true,              name: 'example2',              async: true,              src: '//example.com/script2.js'          })]).then(function () {              console.log('(3) scripts loaded');          })      },        insertscript: function (script) {          // var def = $.deferred(),          protocol = (document.location.protocol === 'https:' ? 'https:' : 'http:');            // dont script 2 on mobile.          // if (script.name === 'example2' && script.is_mobile) {          //    console.log('skipping script');          //    return def.resolve();          // }            var promise = $.when( script.name === 'example2' && script.is_mobile ? $.deferred(function (d) {              settimeout(function () {                  d.resolve('(2) skipping script', protocol + script.src)              }, math.floor(math.random() * 1000))          }).promise() : $.deferred(function (d) {              settimeout(function () {                  d.resolve('(2) single script loaded', protocol + script.src)              }, math.floor(math.random() * 1000))          }).promise())                    return promise.then(function(msg) {            console.log(msg)          });      }  };    dfd.initialize();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>

1.72

var dfd = {      initialize: function () {          console.log('appview::initialized!');          _this = dfd;            $.when(_this.processcookies())          // reference function name, not invoked          .then(_this.loadadscripts)          .then(_this.createchildviews);      },        processcookies: function () {          // no asynchronous operations appear here,           // no need include `$.deferred()` or `.promise()` object          var def = $.deferred(function (d) {              settimeout(function () {                  d.resolve('(1) process cookies')              }, math.floor(math.random() * 1000));          }).promise();          def.then(function (msg) {              console.log(msg);          });          return def.promise()      },          /**       * instantiates new instances of child views.       */      createchildviews: function () {          _this = dfd;          console.log('(4) creating child views');      },        loadadscripts: function () {          _this = dfd;          return $.when.apply(_this, [_this.insertscript({              name: 'example1',              async: false,              src: '//www.example.com/script1.js',          }),          _this.insertscript({              is_mobile: true,              name: 'example2',              async: true,              src: '//example.com/script2.js'          })]).then(function () {              console.log('(3) scripts loaded');          })      },        insertscript: function (script) {          // var def = $.deferred(),          protocol = (document.location.protocol === 'https:' ? 'https:' : 'http:');            // dont script 2 on mobile.          // if (script.name === 'example2' && script.is_mobile) {          //    console.log('skipping script');          //    return def.resolve();          // }            var promise = $.when( script.name === 'example2' && script.is_mobile ? $.deferred(function (d) {              settimeout(function () {                  d.resolve('(2) skipping script', protocol + script.src)              }, math.floor(math.random() * 1000))          }).promise() : $.deferred(function (d) {              settimeout(function () {                  d.resolve('(2) single script loaded', protocol + script.src)              }, math.floor(math.random() * 1000))          }).promise())                    return promise.then(function(msg) {            console.log(msg)          });      }  };    dfd.initialize();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>


Comments