Search code examples
javascriptjqueryajaxdeferredchaining

Chaining jQuery deferred by updating deferred inside then()?


I am learning to use jQuery Deferred and I am thinking about chaining multiple deferreds. Here is a simplified version of my problem:

var def1 = $.ajax(...); // ajax call 1
var def2 = null, def3 = null;

$.when(def1)
  .then(function() {
    def2 = $.ajax(...); // ajax call 2
    return def2;
  }).then(function() {
    def3 = $.ajax(...); // ajax call 3
    return def3;
  });

Then somewhere below the above code, I have:

$.when(def1, def2, def3)
  .then(function() {
    // run some code... .e.g. run foo();
  });

The above code is wrong. It can't do what I want. My actual intention is basically:

I want to run ajax call 1 first.

If ajax call 1 is successful, then run ajax call 2. (Would be a bonus to add on: If ajax call 1 is not successful and/or its returned result doesn't pass my manual validation, alert the user and run some code, e.g. bar().)

If ajax call 2 is successful, then run ajax call 3. (same bonus applies)

And then later I want to run some code ( e.g. foo() ) only when def3 has been resolved. Before running foo(), I don't actually care if def1 and def2 has been resolved or not. (If def3 is rejected, I want to run another piece of code too, if possible. )

The reason that I can't initialise def2(ajax call 2) and def3(ajax call 3) at the beginning is that I want to modify the options of ajax call 2 depending on the returned result from ajax call 1. The Same manner for ajax call 3, depending on ajax call 2's result.

With my limited knowledge and understanding of deferred, the above code basically can't produce what I want.

Because def1 is resolved before def2 is initialised and fired, so the bottom piece of code will run foo() way too soon before def2 is initiated...

So, I am asking for help and advice. See if anyone has any idea how to rewrite the code above and make it run like my intention written above. I am sorry for my incapability.

Thank you all in advance.


Solution

  • You don't need to use when, which is really better used when you want to run multiple deferreds in parallel. Try this:

    var d1 = $.ajax(...);
    var d2 = d1.then(function () {
      if (invalid) {
        return $.Deferred().reject("Invalid");
      }
      return $.ajax(...);
    });
    var d3 = d2.then(function () {
      if (invalid) {
        return $.Deferred().reject("Invalid");
      }
      return $.ajax(...);
    });
    
    d3.done(function () {
      // This is called when we're all done.
    });
    

    Which could be simplified to this:

    $.ajax(...).then(function () {
      if (invalid) {
        return $.Deferred().reject("Invalid");
      }
      return $.ajax(...);
    }).then(function () {
      if (invalid) {
        return $.Deferred().reject("Invalid");
      }
      return $.ajax(...);
    }).done(function () {
      // This is called when we're all done.
    })
    // Use this to handle failure. You can also add to individual
    // deferred objects to handle each failure differently.
    .fail(function () {
      // Do something.
    });
    

    To handle the failures, you may need to add another callback inside the then:

    .then(function () {
      if (invalid) {
        return $.Deferred().reject("Invalid");
      }
      // Do stuff
    }, function (err) {
    
    })