Deferred

briancavalier edited this page Apr 2, 2012 · 8 revisions

A Deferred represents a computation or unit of work that may not have completed yet. Typically (but not always), that computation will be something that executes asynchronously and completes at some point in the future. For example, XHR operations (asynchronous ones, anyway, which is typical) in a browser never complete in the current turn of the Javascript event loop. Thus, an asynchronous XHR is one type of deferred computation.

A Deferred typically has a single producer (although having many producers can be useful, too) and many consumers. A producer is responsible for providing the result of the computation, and typically, but not always, will be the same component that created the Deferred. As the name implies, a consumer observes the result of the computation.

Producers

Producers provide a result via the Resolver API: deferred.resolve() or deferred.resolver.resolve(). The two are functionally equivalent, but deferred.resolver can safely be given out to an untrusted component without giving away full access to the Deferred. The resolver is also frozen and thus cannot be corrupted. That provides a clear separation of concerns by allowing a component to produce a result but not to know any details about consumers.

var deferred = when.defer();

// The deferred's resolver, which can safely be given to other components
var resolver = deferred.resolver;

Consumers

Consumers can observe the result via [[deferred.promise|promise]]. The promise can be given to any number of components, who can observe the result using when(). They can also use .then(), but [[read why when()|when]] is usually a better choice. Like deferred.resolver, even though when.js's Deferred implements the Promise API, it is better to give only the deferred.promise to consumers so that they can't modify the Deferred (such as calling resolve or reject on it!).

var deferred = when.defer();

// The deferred's promise, which can safely be given to other components
var promise = deferred.promise;

Resolving and rejecting

When a producer provides the result by calling deferred.resolver.resolve() (or deferred.resolve()), all consumers are notified by having their callbacks (which they registered via when()) called with the result.

A producer may also reject the Deferred, signalling that the Deferred's computation failed, or could not complete given some set of constraints. In this case, all consumers will be notified by having their errorback called (the 3rd parameter passed to when(), or the 2nd parameter passed to .then()).

Progress

when.js's Deferreds also support progress notifications, to indicate to consumers that the computation is making progress toward its result. A producer may call deferred.resolver.progress() (or deferred.progress()) and pass a single parameter (whatever it wants) to indicate progress. All consumers will be notified by having their progress handler called (the 4th parameter to when(), or the 3rd parameter to .then()).