Skip to content
briancavalier edited this page Feb 3, 2012 · 2 revisions

Sometimes it can be useful to execute some cleanup code when a Deferred is rejected under certain conditions. For example, if you're creating a Promises-based XHR library, it may be useful for you to distinguish between rejecting a promise because the XHR is canceled inflight, and rejecting it because the server returns an HTTP status other than 20x. In one case, the XHR itself was never completed, and in the other it was completed, but returned a failed result.

The when/cancelable decorator makes a Deferred cancelable, adding a cancel() method to Deferred. The cancel() method behaves exactly as reject except:

  • It executes a canceler function that you provide before all other rejection handlers (errbacks).
  • The canceler should return a reason (i.e. Error, error code, etc.) that will be forwarded on to all rejection handlers.

This effectively creates two rejection modes:

  1. a normal rejection mode when using reject(), which behaves as usual, and
  2. a new canceled rejection mode when using cancel(), which executes the canceler function first, then behaves like a normal rejection.

For example, if we were implementing the XHR lib described above, we might do something like the following:

// Pull in when/cancelable
var cancelable = require('when/cancelable');

var inFlight = [];

function cleanupInflightXhr(xhr) {
	var canceledStatus = // ...

	// ... cleanup ...

	// This will be forwarded to all rejection handlers
	return canceledStatus;
}

function xhrGet(url, options) {
	var xhr = setupXhr(url, options);

	// Create a cancelable deferred and register a canceler function
	// that will cleanup the XHR if it is canceled inflight.
	var deferred = cancelable(when.defer(), function() {
		cleanupInflightXhr(xhr);
	});

	inFlight.push(deferred);

	// ... do other setup ...

	// Return ONLY the promise to callers so they can observe the
	// xhr result, but cannot resolve, reject, or cancel it.
	return deferred.promise;
}

function cancelAllInflight() {
	// Cancel and cleanup all inflight XHRs.  This will cause all
	// cancelers to be invoked and then rejection handlers registered
	// by callers.
	inFlight.forEach(function(inflightDeferred) {
		inflightDeferred.cancel();
	});
}
Clone this wiki locally