It seems that jQuery's Deferred actually can handle all the methods that a Deferred object and Promise object can do, but it only returns a Promise object to the user so that a subset of methods is given, so that the user cannot resolve or reject -- for what the user not supposed to do.
So Deferred is actually quite clear as a class. What can be confusing is another class Promise and it made things seem more complicated because now there are two classes for this pattern and it may seem confusing.
I wonder
jQuery’s promises were partially informed by prior art including Q promises and Dojo and Twisted Python deferreds, which in turn were inspired by promises in E. In E and subsequently Q, the promise and the resolver are separate objects based on the Object Capability Programming (OCap) design principle. In Q, promise
and resolve
are the separate capability-bearing objects, and the “deferred” is just a container for these capabilities. In the ES6 Promise
design, the deferred exists but is absent from the interface to eliminate confusion—instead, the promise constructor takes a callback to which resolve
is sent as an argument.
E was designed for object oriented security and robust composition of agents in multiple processes. Promises are an important primitive for ensuring all of E’s goals. The object capability paradigm is an alternative to the access control list paradigm. Instead of user-oriented authorities, capabilities are broken down into individual objects. Granting authority is as simple as giving an agent access to an object that bears a capability, and OCap depends on having an environment where it is possible to guarantee that capabilities can be obtained only by explicit and deliberate interaction with other objects. Revoking an authority is also possible with object membranes created using weak maps (to ensure that memory does not leak) and proxies (to create boundaries where cross-membrane references are always wrapped and can be later broken).
Observation and notification are separate capabilities. It is possible to give some agents the authority to observe the resolution of a promise, and give other agents the ability to resolve a promise. It is important in a secure system that the communication channel is uni-directional. One resolver can communicate one value to all observers, but no observer or resolver should be able to otherwise interfere with the invariants of any other agent.
Promises are a variant of the Gamma et Al Observer pattern or the Publisher, Subscriber pattern. There are noteworthy additional constraints. A promise is a broadcast publisher. Multiple observers can be notified. Promises also guarantee exactly one resolution, either to resolve or reject. Unlike PubSub, a promise will inform an observer regardless of whether it begins observing before or after that one resolution has been dispatched. Unlike an Event Emitter, promises guarantee asynchrony. Event emitters are typically synchronous, both on the DOM and in Node.js.
For the purposes of robust composition and security, a promise guarantees that all handlers will be called in separate events. This prevents a variety of ways a function can interfere with other entities that share the same call stack, like calling functions that close on state that has not necessarily been set up before a function returns, or to inspect their own stack to infer what other agents are doing.
Most JavaScript environments are not sufficient to satisfy the security guarantees of the promise design. Google Caja and DrSES are projects to create such environments. However, Q promises were designed to be compatible with the original design such that code written for users of Q could more easily be ported to such environments. For the same reason, Q, unlike other JavaScript promise libraries, is also designed like E promises based on a message passing “kernel” that allows them to be used to send messages to objects in other processes, returning promises for the results which can be used as proxies for further messages to the results.
jQuery adopted promises but only partially appreciated the prior art. The promises in jQuery were reevaluated around jQuery’s own event dispatch patterns, with multiple inputs and outputs by default, which breaks the analogy that a promise corresponds to the result of a function call, either the return value or the thrown exception. jQuery promises also do not capture exceptions, so it is necessary to explicitly create “rejections” if observers should be informed of a failure. This makes it impossible for some agents to recover from certain errors. jQuery also decided to follow Dojo and Twisted in conflating the promise and resolver into one object, which was intended to make the interface easier to use. jQuery promises did not originally create new promises for the return value of the fulfilled or rejected handlers, but that mistake was later rectified.
is the above accurate?
Yes. You could say that jQuery.Deferred
is a subclass of the jQuery Promise
.
is it a design pattern of its own
Probably, but a badly chosen one. How Deferreds and Promises should work is as two separate interfaces - you either have the object that can resolve things or you have the object that gets resolved (and which you can observe). The deferred could even be seen as a builder for the promise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With