2 * Copyright (C) 2013 Google Inc. All rights reserved.
11 // This design for a DOM-compatible Promise type aims to be
12 // usable in the majority of the web's single-response asynchronous APIs,
13 // either directly or through subclass (e.g., to add progress notification).
15 // It also aims to enable end-users (non-spec authors) to build systems
16 // using these types directly without appealing to libraries or DOM magic
17 // for creating/fulfilling Promises.
19 // We identify the following features as required:
21 // * Chaining of .then() calls
22 // * Subclassing by DOM API designers (see ProgressPromise.idl, e.g.)
23 // * Eventual import or subsetting by TC39 for adding Promises to the
28 // Unifying or describing the internal behavior of events is a non-goal.
29 // Promises are a single-request/single-repose formalism and may capture
30 // values from past resposes. Events, on the other hand, describe a series
31 // of future events. They cover separate use-cases and we do not seek to
34 // Compatibility with existing promises/futures/deferreds libraries or
35 // terminology is a non-goal. Where adopting existing terminology improves
36 // the usability of this design, we will. Otherwise, appeals to
37 // compatibility with published libraries, specifications, and practice are
38 // not compelling. So far, this is compatible with the
39 // Promises/A+ spec, although largely by accident and in part because it
40 // leaves core compatibility points (such as defining what it means to be a
41 // testable "Promise" type in return handling) undefined:
43 // https://github.com/promises-aplus/promises-spec
47 // DOM Promises represent the completion of a single operation, past or
48 // future, an error handling for that operation.
50 // Promises take an initialization function as their only parameter. This
51 // function is called back synchronously (by the time construction finishes)
52 // with references to the the resolve and reject functions that can later be
53 // used to resolve the Promise.
55 // function doAsyncWork() {
56 // return new Promise(function(r) {
57 // setTimeout(function() { // ...time passes
59 // r.resolve("success");
64 // // Callers of the function can be notified of resolution easily:
65 // doAsyncWork().then(console.log.bind(console));
67 // Promises provide a way for others to be notified when the resolution
68 // occurs without handing them the ability to resolve the future itself.
69 // This is accomplished either by providing callbacks to the ".then()"
74 // the delivery of any resolution or error must be "apparently
75 // asynchronous", specifically they must be delayed at least to the "end of
76 // microtask" as defined for the delivery of mutation observer and
77 // object.observe() notifications. delivery at any point beyond that, e.g.
78 // in the next turn or microtask, is allowed, but all implementations must
79 // demonstrate the following behavior:
82 // var f = new Promise(function(c) { callbacks = c; });
83 // // Polyfills in debugging mode provide a "_state" property to track
85 // assertTrue(f._state == "pending");
86 // callbacks.resolve(null);
87 // assertTrue(f._state == "pending");
90 // callbacks.resolve(null);
94 // // Catch the AlreadyResolved error thrown by the second resolve()
95 // assertTrue(e instanceof AlreadyResolved);
100 // Chaining is a requirement, meaning that it must be possible to call
101 // ".then()" and receive a sensible result from which you can ".then()"
104 // Promises return a new automatically-generated Promise from each
105 // "then()" call. The then()-generated Promises are resolved by the
106 // callbacks passed to each "then()" invocation. These functions are called
107 // based on the resolution and their return values are passed onward to the
108 // next auto-generated Promise:
110 // // Example invocation
111 // var doItAsync = function() {
113 // var f = new Promise(function(c) { callbacks = c; });
118 // .then(fulfill, reject)
119 // .then(fulfillIntermediate)
120 // .then(fulfillFinal);
122 // Chaining fast-forwards un-handled values to the next item in the chain
123 // which registers a handler for that particular disposition (fulfill &
124 // reject). As in RSVP.js, the "onerror" function passed to then() is
125 // called here, logging the exception:
128 // .then(function(){ throw new Error("spurious!"); })
129 // .then(fulfill) // "fulfill" is not called, and we fast-forward
130 // .then(null, console.error.bind(console));
132 // If the second call were to handle "error", the final callback would not
133 // receive an "error" unless it also rejected its Promise. e.g.:
136 // .then(function(){ throw new Error("spurious!"); })
137 // .then(fulfill, console.error.bind(console)) // logs the error
138 // .then(null, console.error.bind(console)); // does nothing
140 // Return Value Sniffing, value/error Chaining, and Forwarding:
142 // Like many other Promise systems, the callbacks for fulfill & error
143 // are used to resolve the auto-generated Promise returned from a call to
144 // then(). Below is the basic logic for then() callback return handling.
147 // * If the return is a Promise, merge the generated Promise's behavior
149 // * If the callback throws, reject() the generated future with the thrown
150 // value, even if the value is not an Error.
151 // * If the return value is any other kind, use it as the "value" for
152 // resolving the generated Promise.
154 // TODO(slightlyoff): updated example logic
156 // Similar logic is in place when using the resolve() method to provide a
157 // value to resolve the Promise with; if a value passed to resolve() is a
158 // Promise, the value of the "local" future assumes the value of the "far"
159 // future. If it is any other value, it will be passed to fulfill(). The
160 // behavior of fulfill is to move the Promise to the "fulfilled" state and
161 // set .value to whatever value is passed (regardless of type).
163 // Pseudo-code for resolve():
165 // resolverInstance.resolve = function(value) {
166 // if (isBrandedAsPromise(value)) {
167 // value.then(this.resolve, this.reject);
170 // this.fulfill(value);
171 // }.bind(resolverInstance);
175 // 1) then() callbacks in the order of registration
176 // 2) then() callbacks added after initial dispatch phase ends
178 // 2 provides for a Promise to have then() callbacks added after it is
179 // resolved, allowing programmers to treat a Promise object as something
180 // which is safe to call .then() on at all times. For example, both calls to
181 // then() in the following example will succeed and both callbacks will
182 // receive the resolution value, albiet at different times:
185 // var f = new Promise(function(c) { callbacks = c; });
186 // setTimeout(callbacks.fulfill.bind(callbacks, "Success!"), 1);
187 // f.then(function(value) {
188 // // Just to illuminate the timing, we make sure that our next then()
189 // // call occurs well outside the current turn.
190 // setTimeout(function() {
191 // f.then(console.log.bind(console));
195 // We observe that the second then()-added callback *DOES* log to the
196 // console, but well after the initial resolution. All conforming
197 // implementations MUST provide the ability for post-resolution then() calls
198 // to resolve this way.
200 // Cancelation and Timeouts:
202 // The passed set of callbacks includes cancel() and timeout() functions.
203 // These are syntactic sugar that generate Error objects with known name
204 // values ("Cancel" and "Timeout", respectively). Handling these values is
205 // done through the usual reject handler of a Promise. Using only fulfill()
206 // and reject() we can re-create these sugar functions easily:
209 // var f = new Promise(function(c) { callbacks = c; });
210 // // Compatible, ad-hoc versions of cancel() and timeout()
211 // var cancel = function() { callbacks.reject(new Error("Cancel")); };
212 // var timeout = function() { callbacks.reject(new Error("Timeout")); };
214 // // Register a reject handler that understands cancelation and timeout:
215 // f.then(fulfill, function(reason) {
216 // switch(reason.name) {
218 // // handle cancelation...
228 // Calling either the ad-hoc cancel() or callbacks.cancel() will have the
231 // Errors and Exceptions:
233 // As seen in the example code above, exceptions throw by callback handlers
234 // are caught by the system. This leads to some particular debugging hazards
235 // which other systems have treated in various ways. Some have opted simply
236 // not to swallow errors. Some propagate errors in various ways (this design
237 // employs a variant of that approach).
239 // No matter what approach is pursued, integration with developer tooling is
240 // key. Developer consoles SHOULD log un-caught errors from future chains.
241 // That is to say, if future.then(onresponse); is the only handler given for
242 // a resolver which is eventually rejected, developer tools should log the
245 interface PromiseResolver {
246 // Directly resolves the Promise with the passed value
247 void fulfill(optional any value);
249 // Indirectly resolves the Promise, chaining any passed Promise's resolution
250 void resolve(optional any value);
252 // Rejects the future
253 void reject(optional any error);
255 // Note that implementations may keep internal state here, notably an
256 // "isResovled" flag.
259 callback PromiseCallback = void (PromiseResolver resolver);
260 callback AnyCallback = any (optional any value);
262 [Constructor(PromiseCallback init)]
264 /////////////////////////////////
267 // Returns a Promise whose fulfillment value will be the return value
268 // from whichever of the callbacks is eventually invoked.
269 Promise then(optional AnyCallback fulfill, optional AnyCallback reject);
271 // A shorthand for not registering only an reject callback. Desugars to:
273 // Promise.prototype.catch = function(reject) {
274 // return this.then(undefined, reject);
277 Promise catch(optional AnyCallback reject);
279 /////////////////////////////////
282 static Promise fulfill(any value);
283 static Promise resolve(any value); // same as any(value)
284 static Promise reject(any value);
286 // exposed as "any" in JavaScript, without "_"
287 static Promise _any(any... values);
288 // FIXME: Should be "all"?
289 static Promise every(any... values);
290 static Promise some(any... values);