Killing `Promise.fulfill`

Tab Atkins Jr. jackalmage at gmail.com
Wed Aug 21 17:33:35 PDT 2013


On Wed, Aug 21, 2013 at 5:12 PM, Mark S. Miller <erights at google.com> wrote:
> On Wed, Aug 21, 2013 at 3:44 PM, Tab Atkins Jr. <jackalmage at gmail.com>
> wrote:
>>
>> On Wed, Aug 21, 2013 at 3:36 PM, Domenic Denicola
>> <domenic at domenicdenicola.com> wrote:
>> > On Aug 21, 2013, at 18:31, "Mark S. Miller" <erights at google.com> wrote:
>> >> Good idea. As a coercing function, a natural name is Promise.as(v1).
>> >> Also,
>> >> as a common coercer, brevity is a virtue.
>> >
>> > How about just `Promise`, following `String`, `Number`, `RegExp`, etc.?
>> >
>> > (I tend to agree with Tab that both #a and #b should return a new
>> > promise.
>> > But we do need an easy coercion function, as Mark emphasizes.)
>>
>> Yeah, that's the existing coercer idiom.  The other one that's close
>> is Array.from().  It still always produces a new object, but that
>> doesn't necessarily have to be a property of every class's usage.
>>
>> But I like just Promise(), sans "new".
>
>
> Good. I like it too, and Allen's latest draft class semantics enables the
> definition of classes that can do this reliably. But is there any way to
> reliably polyfill this in ES5? It's not clear. For the std promise API,
> given the urgency with which people want to use it *now*, I think this
> polyfillability is important.

Yes, why wouldn't it be?  The test for promise-ness can be done today,
without special browser privilege, and distinguishing between
`Promise(foo)` and `new Promise(foo)` is a trivial matter of testing
the type of `this` inside the function.  (If it's instanceof Promise,
you were called with new.  Otherwise, you weren't.  This isn't 100%,
as people can fool you with Promise.call(), but that's not something
you need to actually worry about, I think.)

>>  There's no
>> behavioral difference between resolving and accepting for .then(), so
>> we don't need it there, and you already need to be careful that your
>> value is wrapped in a promise for .flatMap() callbacks, so requiring
>> the same for the resolver function when those are the semantics you
>> want is fine with me.
>
> I did not understand this reasoning even though I like its conclusion. Could
> you expand on this, perhaps with examples? Thanks.

Okay.

For .then(), you can't tell the difference between adopting and just
fulfilling with a promise, because both will wait for the inner
promise to fulfill before the outer promise fulfills.  So obviously
you don't need a resolver function for "just wrap it" if you're using
.then().

For .flatMap(), there's a clear difference between the two.  However,
we should probably engineer toward making things similar for the
resolver and a .flatMap() callback.  In a callback, if you want to
return a nested promise, you have to first wrap it in a throwaway
promise (which will then get adopted away).  That is:

p1.flatMap(x=>p2).flatMap(print) // prints whatever value p2 resolves to
p1.flatMap(x=>Promise.of(p2)).flatMap(print) // prints p2 itself, not
its resolved value

Similarly, if you limit yourself to just the resolve/reject resolver functions:

new Promise(r=>r.resolve(p2)).flatMap(print) // prints whatever value
p2 resolves to
new Promise(r=>r.resolve(Promise.of(p2))).flatMap(print) // prints p2
itself, not its resolved value

On the other hand, if you allow the accept resolver function:

new Promise(r=>r.accept(pw)).flatMap(print) // prints p2 itself, not
its resolved value

I'm fine with requiring the bit of extra work that's needed without
accept, due to the symmetry - Resolver#resolve is exactly identical to
returning from a callback, and Resolver#reject is exactly identical to
throwing from a callback, but there's no way to duplicate
Resolver#accept from a callback, and thus it's not clear that this
functionality is actually needed.

>> We'll still need the class static for it, just not the resolver
>> function.  I propose we quit with the synonyms, and use Promise.of()
>> like I (and others) proposed a long time ago.  ^_^
>
> I do not love this but I do not object. +0.8 ;).

Cool.  That means we get a decent naming precedent for the monad ops,
and consistency with Array.of (which also happens to be a monadic
lifter, if you limit yourself to calling it with only a single
argument).

~TJ


More information about the es-discuss mailing list