s/Future/Promise/g

One of the things I’ve poured myself into this year — with a merry band of contributors including Domenic Denicola, Anne van Kesteren, Jake Archibald, Mark Miller, Erik Arvidsson, and many others — has been a design for Promises that DOM and JS can both adopt.

There’s a (very) long history of Promises, Deferreds, and various other Promise-ish things in JS which I won’t bore you with here except to note that there are very few client-side libraries which don’t include such a thing and use it as a core idiom for dealing with async behvaior (e.g., XHR). jQuery, Dojo, Q, WinJS, Cujo, Closure, YUI, Ember (via RSVP), and all the rest use this style of contract pervasively and have for years. In fact, it’s so common that Domenic Denicola and others have gone as far to rustle up a community standard for how they should interop under the banner of Promises/A+. The major libraries are coalescing around that contract and so it seems time, finally, to make our biggest and most important library — DOM — savvy to them too.

The recent history starts (arbitrarily) a couple of years ago and ends 2 weeks ago. In that time, a single design has evolved that not only DOM could get behind, but also which TC39 has agreed in principle to endorse and support going forward, thanks in large part to Mark Miller’s analysis of the competing styles of use which proves strongly that the A+-compatible API we’ve designed need not upset anybody’s applecart.

The TC39 meeting was a key turning point: up until 2 weeks ago, DOM had a version of this design under the name Future. I made the decision to not use the name Promise for that work because without TC39’s agreement on a design, the DOM variant could at some point find itself both camping on a global name and disagreeing with JS about semantics or naming of particular APIs. That sort of thing might have lead to the suspicion by DOM folks that TC39 was out of touch and slow, and by TC39 that DOM rushed in like fools into a space that’s pretty clearly something the std lib should include (even if it couldn’t do so for multiple years due to publication and spec timing issues).

Meanwhile, in the background, several DOM APIs have started to adopt Futu…er…Promises, notably Web Crypto and Web MIDI. There has also been lively discussion about other APIs that can benefit from moving to a standard mechanism for describing async operations.

It seems, in each individual case, like this shouldn’t be such a big deal. Some APIs have callbacks, some use events…what’s the fuss?

The big reason to spend months of my life on this problem, and to harass other very busy people to do the same, is to me the core value of web standards: when they’re working well, they create a uniform surface area that describes a coherent platform. We are the beneficiaries of this uniformity today regarding events, and they are a major piece of the design language which DOM API authors can reliably use to help describe bits of their design. Promises, like Events, are yet another tool in the box that DOM APIs authors can use, and thanks to sane constructors and the ability to subclass built into the design, it’s possible for end-user code to eventually put down the custom implementations of Promise-like things and simply rely on the platform to do what platforms should do: make cheap and easy what previously was common but expensive.

As of this week, the WHATWG DOM spec has changed its naming to reflect the consensus design, substituting Promise for Future, renaming accept() to fulfill(), and removing a few of the methods that didn’t have consensus or were agreed to be unnecessary in a 1.0.

Thanks to this broad consensus over the design, both Mozilla and Google have begun to implement Promises in our respective engines. Further, the W3C TAG agreed at last week’s meeting to recommend to spec authors that they adopt Promises for asynchronous, single-valued operations. This is also great news because the TAG has gone from being a body which is largely reactive to one that has begun to become pro-active, taking a more active role in API oversight and integration across Working Groups to help ensure the coherence of the overall platform’s architecture and design.

The job of moving many of today’s APIs which use ad-hoc callback systems or vend Promise-like-but-not-quite objects is far from over, much the way providing constructors for DOM operations is a work in progress…but I have many reasons to hope, not least of all because folks like Mark, Tab, Domenic, Yehuda, and Anne are working together in good faith to help make it so.

This, then, is how we can collectively add new primitive types to the platform: begin with community and evidence, build consensus around a design slowly (starting with the key stakeholders), and eventually work across the entire platform to integrate these primitives pervasively.

It takes people who are willing to put down an us-vs-them perspective and collaborate honestly and openly to make it happen, but moving the web forward always does. Promises are budding proof that such a thing isn’t beyond our institutions, vendors, and platform leaders to do. Collaboration across the spectrum from users, to spec organizations, to vendors can happen. The platform can be reformed and rationalized, and even the most recalcitrant of DOM spec authors are willing to listen when presented with evidence that their APIs aren’t idiomatic or could be improved to help make the commons better, even when it comes at some risk and cost to their own APIs.

8 Comments

  1. Posted June 8, 2013 at 5:22 am | Permalink

    And I got together with Anne today to help him update the spec to the harmonious compromise. Should be finished sometime this next week. Yay!

  2. Posted June 10, 2013 at 10:19 pm | Permalink

    How do you see Promises interacting with Yield? I find promises + yield to be the most powerful abstraction in building real things.

    http://www.onebigfluke.com/2012/07/futures-as-design-pattern-for.html

  3. Posted June 12, 2013 at 8:59 am | Permalink

    Hey Brett,

    Good question. Promises are the sort of low-level thing you might imagine Yeild de-sugaring into in order to deliver values across CPS-transformed code bodies.

    How they interact when you’re using them together and not playing in one world or the other is a question for how the de-sugaring eventually gets written. What sorts of patterns are you encountering there?

  4. Alex Bell
    Posted June 13, 2013 at 2:30 pm | Permalink

    This is quietly gamechangingly stuff. Keep up the awesome work.

    BTW that linked list of create APIs that don’t have constructors in Firefox is a doozy. The number of SVG elements alone! (And I think of Firefox SVG support as being generally strong.) What’s going on here, and what can be done? Without finger-pointing, maybe there is another good post here.

  5. Posted June 13, 2013 at 4:23 pm | Permalink

    Hey Alex,

    If you scroll down, you’ll see that the list is mostly the same in Blink, indicating spec failings, not just impl issues. What’s needed now is the Extensible Web perspective: it challenges spec authors on these points and lays the groundwork for change.

    If you want to help, filing bugs in the W3C bug trackers against these specs is the next step.

  6. Posted June 14, 2013 at 6:01 am | Permalink

    Hi Alex,
    Thanks for bringing this up.

    I have one year of experience daily working with the Q library, and I found this current spec quite disappointing.

    First, I really like the independence between the “deffered” object (called here the resolver) and the “promise” object which is a object with a .then() function.
    In Q, you create a deffered with a Q.defer(), and you get the promise of it with deffered.promise.

    Here, the spec Promise interface contains both the .then method and the .resolve/.reject functions, why that !?

    Isn’t the “PromiseResolver” interface useless if we can already resolve from a “Promise”, IMO that’s ambiguous you can do the same thing with 2 different APIs.

    Second, I don’t really like that new Promise(callbackTakingAResolver) API. I found very ugly that you need to “externalize” the resolver variable from the callback (e.g. in the Promise.idl design consensus).

    As a bonus, I would love the API to have a .progress like Q does. Q also provides a lot of useful functions, but we can hopefully still build something on top of that native API.

  7. Posted June 14, 2013 at 6:21 am | Permalink

    Also, good luck for making a polyfill of it for IE (8) : catch is a reserved word,

    ({}).catch = 1 fail on IE (tested on IE8).

  8. John Lenz
    Posted June 19, 2013 at 10:12 am | Permalink

    gre-
    The idl now clearly indicates the “resolve” and “fulfill” methods in the Promise interface are static. These would mint new Promise objects and immediately resolve them and wouldn’t act on a existing Promise object.

    Note: ({})[‘catch’] works on IE8.

One Trackback

  1. By YUI Weekly for June 14th, 2013 - YUI Blog on June 14, 2013 at 1:45 pm

    […] s/Future/Promise/g […]