Tag Archives: callback

Staying in the errback chain when chaining Deferreds using then()

19 Apr

I’m gearing up for an article explaining how Deferred chains move between errback and callback chains, and how to deal with some of the more esoteric issues we all come up with.

For now, an interesting snippet:

// Create a dummy deferred in the error state
var d = new dojo.Deferred();
d.errback(new Error("FAIL"));
// And start chaining
d = d.then( function(response) { 
  console.log("success1 :: " + response); 
}, function(error) { 
  console.log("error1 :: " + error); 
});
d = d.then( function(response) { 
  console.log("success2 :: " + response); 
}, function(error) { 
  console.log("error2 :: " + error); 
});

Through reading various articles online, I expected that flow would stay in the errback chain, and error2 would fire with the original error as an argument. However, in all versions of Dojo, the above code will result in:

error1 :: Error: FAIL
success2 :: undefined

Unexpected. If we explicitly return the Error that was originally provided to the first errback function, like so:

// Create a dummy deferred in the error state
var d = new dojo.Deferred();
d.errback(new Error("FAIL"));
// And start chaining
d = d.then( function(response) { 
  console.log("success1 :: " + response); 
}, function(error) { 
  console.log("error1 :: " + error); 
  return error;
});
d = d.then( function(response) { 
  console.log("success2 :: " + response); 
}, function(error) { 
  console.log("error2 :: " + error); 
});

We get:

error1 :: Error: FAIL
success2 :: Error: FAIL

Some online articles state that returning an Error from a callback or errback function will cause the flow to move into the errback chain. This is actually true only when using traditional addCallback() and addErrback() functions.

So, when using then(), we need to do the following:

// Create a dummy deferred in the error state
var d = new dojo.Deferred();
d.errback(new Error("FAIL"));
// And start chaining
d = d.then( function(response) { 
  console.log("success1 :: " + response); 
}, function(error) { 
  console.log("error1 :: " + error); 
  throw error;
});
d = d.then( function(response) { 
  console.log("success2 :: " + response); 
}, function(error) { 
  console.log("error2 :: " + error); 
});


error1 :: Error: FAIL
error2 :: Error: FAIL

This will result in some extra errors in your console log. But it may make things work as you expect.

For more information, and some quite detailed test cases, take a look at the dojo.Deferred tests.

Returning a Deferred object within a Deferred Callback does The Right Thing

6 Feb

dojo.Deferred is very cool, but it’s very easy to tie yourself up in knots. It’s worth spending some time reading about it. The official API doc is good, as is this article which discuss what’s new in Dojo 1.5 on the topic of Deferreds.

One thing in particular that isn’t always obvious is that a callback function itself can return a Deferred. In this case, Dojo will do the right thing, and insert this into the callback chain in the right place. This can make your code much simpler and avoid nasty hacks with co-dependent Deferreds. To illustrate, let’s start off with a simple example that doesn’t exploit that feature:

function sayHello() {
    var deferred = new dojo.Deferred();
    var name = "Fred";
    console.debug("Hello", name);
    deferred.callback(name);
    return deferred;
}

sayHello().then(function(name) {
    console.debug("Goodbye", name);
});

This should print “Hello Fred”, then “Goodbye Fred” in your debugger. Note that we are firing the callback explicitly above. In reality, this is far more likely to be based on the result of an asynchronous call – typically an XHR request. Treat the sayHello() method as a black-box for the purposes of this illustration – the important point is that it returns a Deferred (and that aspect of the function signature can’t be changed).

Now let’s imagine that you need to insert sayHello() into the middle of a Deferred chain. Let’s remove the final three lines from the code above, and add this additional code:

function prepareForArrival() {
    var deferred = new dojo.Deferred();
    console.debug("Preparing for the arrival of an unknown guest");
    deferred.callback();
    return deferred;
}

prepareForArrival().then(sayHello).then(function (name) {
    console.debug("Goodbye", name);
});

You should now see: “Preparing for the arrival of an unknown guest”, then “Hello Fred”, then “Goodbye Fred”.

This works because the then() method takes a callback function as its first argument. Often, you will specify a function which returns a regular value to be passed along to the next callback (i.e. the next then() method) in the chain. However, in this case, we have specified a function which returns a Deferred. Dojo realises this, and inserts that Deferred into the call chain instead. It is executed next, and the result of the callback method from that Deferred is passed into the “goodbye function”.

Remembering this trick can greatly simplify the readability and structure of your code, and allow you to construct complex Deferred chains in a straightforward manner.

Follow

Get every new post delivered to your Inbox.

Join 78 other followers