Promises Consensus

Tab Atkins Jr. jackalmage at gmail.com
Wed Jul 31 10:39:30 PDT 2013


Heya!  I, Mark, and others have been hashing out our remaining
differences on Promises privately, and are all happy with each other
now, with only two remaining issues to be decided in a larger
audience.  Anne says that we should be able to get DOM Promises on
track with this consensus if we finish up the discussion in the next
month or so, since the differences from the current spec are mostly
internal/new API.

Here's our current consensus:

Promises have both a .then() and a .flatMap() method.

1. p.flatMap() does "single-level" resolution:
    * whatever p resolves to, gets passed to the flatMap() callbacks.
    * The callback return value *must* be a promise-like, which is
adopted by the output promise; otherwise, the output promise rejects
with a TypeError.

2. p.then() does "recursive" resolution on the input side (per
consensus following 2 TC39-meetings ago):
    * if p accepts to a promise-like, the callbacks get moved down to
that until it either accepts with a non-promise-like, or rejects.
    * Rejection calls the rejection callback without delay; no extra
resolution mechanics happen here.
    * The callback return value can be a promise-like or not.  If it
is, the output promise adopts it; if not, the output promise accepts
it.

3. The helper functions (Promise.every(), etc.) use .then() semantics.
 That is, Promise.every() will eventually resolve to an array of
non-promise-likes.


The first issue still up for community discussion involves the
definition of "promise-like".

We'd like the definition to be: (a) a Promise or subtype, or (b) a
branded non-Promise (with the branding done via Symbol or similar).
Promises/A+ wants the branding to be done via a method named "then".

This, unfortunately, goes directly against TC39 practices in a number
of other areas, such as iterators, where we don't want short string
names as branding due to the possibility of collision.  (In the case
of "then", collision isn't a possibility, it's a certainty - we *know*
there are libraries out there today that put a "then" method on their
objects without referring to Promises.)  Thoughts?


The second issue still up for community discussion is what "adopts"
means, precisely.

1. Assume a .then() callback returns a non-native promise-like.  We
can't just use magic internal operations to detect when the returned
value resolves, so the output promise will have to register callbacks
on it.  This appears to break our desire to have "lazy" promises in
the future that don't compute a value until someone asks for it.
Should we specify that adoption is done late?  (That is, the output
promise would hold onto the returned promise without touching it,
until someone actually registers some callbacks on it.)  This may have
performance implications - is it possible that we just do eager
resolution now, but later have detection for lazy promises getting
returned and switch to lazy behavior in just those cases?

2. Assume a .flatMap() callback returns a non-native promise-like.
Obviously, the output promise adopts it by registering .flatMap()
callbacks on it.  But what if the promise-like only has a .then()
method?  Should we reject with a TypeError, or fall back to using
.then() resolution semantics?  (I suspect we need to do the former to
maintain monad laws.)

~TJ


More information about the es-discuss mailing list