Question

I am working with a framework that uses jQuery Deferred Objects.

In my code, one async operation should executed successfully so that another async operation be executed.

function doOperation() {

    var def2mock = $.Deferred();
    var def1 = doSomeAsyncOperation().done(function () {
        var def2 = doAnotherAsyncOperation();
    });

   return $.when(def1, def2mock);
}

So I put the call for the second operation in the "done" promise of the deferred of the first operation as in the example. Now, I wrap this sequence with the function doOperation. The result of doOperation should be the join of the two deferreds of both async calls. Meaning doOperation succeeds if all async operations succeed and fails if any of them fail, which is exactly what $.when does.

The problem is that in order to create the join with $.when, I need both deferreds present at the time $.when is called. And because the deferred of the second async operation is not available at that moment, I had to find a way to create the join first, then add the deferred of the second async operation to the join later.

To do this, I thought I can define a new mock $.Deferred named def2 in doOperation. And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one. The only way I found was to do

def2.done(def2mock.resolve)
// and 
def2.fail(def2mock.reject)

but I don't feel this manual linking is the right way to do it.

So please tell me if you have a better suggestion on how to do it the right way.

Was it helpful?

Solution

And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one.

This is actually what .then does: It returns a new promise which gets resolved/rejected when the promise returned from the passed callback is resolved/rejected.

In order to make this work with $.when, you have to keep a reference to the original promise:

function doOperation() {
  var def1 = doSomeAsyncOperation();
  var def2 = def1.then(function () {
    return doAnotherAsyncOperation();
  });
  return $.when(def1, def2);
}

DEMO

def2 will be resolved when the promise returned by the callback is resolved, i.e. the one that doAnotherAsyncOperation returns.


But I don't think using $.when is a good choice here, from a conceptional point of view. You are actually executing two asynchronous functions sequentially, but $.when is used to run asynchronous functions in parallel.

I would just chain the function calls via .then and collect the responses:

function doOperation() {
  return doSomeAsyncOperation().then(function (resp1) {
    return doAnotherAsyncOperation().then(function(resp2) {
      return [resp1, resp2];
    });
  });
}

DEMO

I'd say it's clearer here that doAnotherAsyncOperation is executed after doSomeAsyncOperation and the result of both calls is available to the caller of doOperation.

You might have to do something similar for the fail case.

OTHER TIPS

I don't have any experience with Deferred (nor do I fully understand what it does), but if you are just looking to chain operations, would something like this work?

function doOperation(operations) {
    var wrapper = {};
    wrapper.index = 0;
    wrapper.operations = operations;
    wrapper.onthen = $.proxy(function() {
        if (this.index < this.operations.length) {
            this.operations[this.index]().then(this.onthen);
        }
        this.index++;
    }, wrapper);
    wrapper.onthen();
}

Here's a demonstration: http://jsfiddle.net/vz6D7/2/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top