1 #### NOTE: The IDL hosted here is now advisory. This repo hosts refactoring examples and designs, a high-fidelity polyfill, and rationale for this work. [See the living DOM spec for the current version](http://dom.spec.whatwg.org/#futures) if you are implementing Promises in a runtime.
3 <img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png"
4 align="right" alt="Promises/A+ logo, since DOMFutures are compatible" />
8 A Promises Design for DOM, currently in IDL. Also a p(r)ollyfill
9 and re-worked APIs to take advantage of the new semantics.
11 ## Promises? [I Don't Speak Your Crazy Moon Language](http://www.pigdog.org/auto/mr_bads_list/shortcolumn/1914.html).
13 A Promise is an object that implements a standard contract for
14 the results of an operation that may have already occurred or may happen in the
17 There's a lot in that sentence that's meaningful, but the big ticket items are:
19 * Promises are a contract for _a single result_, either success or failure. If
20 you need to model something that happens multiple times, a single Future is
21 not the answer (try events or an iterator that creates a stream of Promises).
22 * Promises provide a mechanism for multiple parties to be informed of a value,
23 much the same way many bits of code can hold a reference to a variable
24 without knowing about each other.
25 * Promises aren't, in themselves notable. Instead, they derive their value
26 through being _the one way_ to encapsulate potentially asynchronous return
29 To do all of this without syntax support from the language, Promises must be
30 objects with standardized methods. The current design provides a constructable
31 class that allows end-user code to vend instances of `Future` from their own APIs
32 as well as a few standard methods:
35 // Accepts "accept" and "reject" callbacks, roughly the same thing as success
36 // and failure for the operation.
37 futureInstance.then(onaccept, onreject);
39 // Accept and reject callbacks only ever have a single parameter, which is the
40 // value or reason, respectively:
43 doSomethingWithTheResult(value);
45 function(reason) { console.error(reason); }
48 // .then() always returns a new Future, one which takes whatever value the
49 // callback it's registered with returns:
50 futureInstance.then(function(value) {
51 return processValue(value);
53 .then(function(processedValue) {
57 // A key part of the Futures design is that these operations are *chainable*,
58 // even for asynchronous code. This makes it easy to compose asynchronous
59 // operations in readable, linear-looking code:
60 futureInstance.then(aHandlerThatReturnsAFuture)
61 .then(doSomethingAfterTheSecondCallback);
63 // .catch() gives you access only to errors:
64 futureInstance.catch(onreject);
67 ## OK, So Why Promises For DOM?
69 To understand why DOM needs Futures, think about a few of the asynchronous APIs
70 in DOM that return single values:
72 * [XHR](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest)
73 * [Geolocation](http://www.w3.org/TR/geolocation-API/)
74 * [IndexedDB](http://www.w3.org/TR/IndexedDB/)
75 * [`onload`](https://developer.mozilla.org/en-US/docs/DOM/window.onload)
77 There are some similarities today in how these APIs work. XHR and `onload` share
78 the idea of a `readyState` property to let code detect if something has happened
79 in the past, giving rise to logic like this:
82 if (document.readyState == "complete") {
85 document.addEventListener("load", doStartupWork, false);
89 This is cumbersome and error-prone, not to mention ugly. IndexedDB's
90 [`IDBRequest` class](https://developer.mozilla.org/en-US/docs/IndexedDB/IDBRequest)
91 also supports a `readyState` property, but the values range from
92 [1-2](https://developer.mozilla.org/en-US/docs/IndexedDB/IDBRequest#readyState_constants),
93 not [0-4 as used in XHR](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#Properties)
94 or [strings as used for documents](http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#current-document-readiness).
95 Making matters worse, the callback and event names don't even match! Clearly
96 DOM needs a better way to do things.
98 A uniform interface would allow us to manage our callbacks sanely across APIs:
101 // Example of what the future might hold, not in any current spec
102 document.ready().then(doStartupWork);
104 // By returning a Future subclass, XHR's send() method gets more usable too:
105 var xhr = new XMLHttpRequest();
106 xhr.open("GET", filename, true);
107 xhr.send().then(handleSuccessfulResponse,
108 handleErrorResponse);
110 // Geolocation can be easily refactored too:
111 navigator.geolocation.getCurrentPosition().then(successCallback, errorCallback);
113 // Even IDB gets saner:
114 indexedDB.open(databaseName).then(function(db) {
119 Providing a single abstraction for this sort of operation creates cross-cutting
120 value across specifications, making it easier to use DOM and simpler for
121 libraries to interoperate based on a standard design.
123 ## OK, But Aren't You Late To This Party?
125 There's a [long, long history of Promises](http://en.wikipedia.org/wiki/Futures_and_promises)
126 both inside and outside JavaScript. Many other languages provide them via
127 language syntax or standard library. Promises are such a common pattern inside
128 JavaScript that nearly all major libraries provide some form them and vend
129 them for many common operations which they wrap. There are differences in
130 terminology and use, but the core ideas are mostly the same be they
131 [jQuery Deferreds](http://api.jquery.com/category/deferred-object/),
132 [WinJS Promises](http://msdn.microsoft.com/en-us/library/windows/apps/br211867.aspx),
133 [Dojo Deferreds or Promises](http://dojotoolkit.org/documentation/tutorials/1.6/promises/),
134 [Cujo Promises](https://github.com/cujojs/when),
135 [Q Promises](https://github.com/kriskowal/q/wiki/API-Reference), [RSVP Promises (used heavily in Ember)](https://github.com/tildeio/rsvp.js), or even in [Node Promises](https://github.com/kriszyp/node-promise). The diversity of implementations has led both to incompatibility and efforts to standardize, the most promising of which is the [Promises/A+ effort](https://github.com/promises-aplus/promises-spec), which of course differs slightly from [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) and greatly from [other pseudo-standard variants proposed over the years](http://wiki.commonjs.org/wiki/Promises).
137 Promises/A+ doesn't define all of the semantics needed for a full implementation,
138 and if we assume DOM needs Promises, it will also need an answer to those
139 questions. That's what this repository is about.
144 // New APIs that vend Futures are easier to reason about. Instead of:
145 if (document.readyState == "complete") {
148 document.addEventListener("load", doStartupWork, false);
151 // ...a Future-vending ready() method can be used at any time:
152 document.ready().then(doStartupWork);
154 // Like other Promises-style APIs, .then() and .done() are the
155 // primary way to work with Futures, including via chaining, in
156 // this example using an API proposed at:
157 // https://github.com/slightlyoff/async-local-storage
158 var storage = navigator.storage;
159 storage.get("item 1").then(function(item1value) {
160 return storage.set("item 1", "howdy");
163 // The previous future is chained to not resolve until
164 //item 1 is set to "howdy"
165 storage.get("item 1").done(console.log);
169 Promises can also be new'd up and used in your own APIs, making them a powerful
170 abstraction for building asynchronous contracts for single valued operations;
171 basically any time you want to do some work asynchronously but only care about
172 a single response value:
175 function fetchJSON(filename) {
176 // Return a Promise that represents the fetch:
177 return new Promise(function(resolver){
178 // The resolver is how a Promise is satisfied. It has reject(), fulfill(),
179 // and resolve() methods that your code can use to inform listeners with:
180 var xhr = new XMLHttpRequest();
181 xhr.open("GET", filename, true);
183 xhr.onreadystatechange = function() {
184 if (xhr.readyState == 4) {
186 resolver.resolve(JSON.parse(xhr.responseText));
195 // Now we can use the uniform Future API to reason about JSON fetches:
196 fetchJSON("thinger.json").then(function(object) { ... } ,
197 function(error) { ... });