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

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.