[CommonJS] Promise Bake-off

3 views
Skip to first unread message

Kris Kowal

unread,
May 1, 2010, 10:21:26 PM5/1/10
to comm...@googlegroups.com
So, having myself converted from the Thenable camp to the Opaque
Promise camp, I probably won't be converting back. However, my
personal religious choices should not hold up progress! I have a
proposal:

Let's reserve the name "common/promise" for a module with an API
similar to Promises/B but build on Promises/A thenables.

Furthermore, let's reserve the name "common/q" for a a module based on
Promises/B exactly.

Let it be known that "common/q" does not interoperate with thenables,
and that "common/promise" is strictly more, shall we say, "inclusive".
It's my intention to continue building out experiments with "q-"
prefixed modules, so we can at least take this conflict off-list and
into the market of adoption. Let JSGI be build on "thenables", as it
has been drafted. Let Q-JSGI refer to the JSGI approach with
thenables replaced with "common/q" promises.

Would this break the deadlock?

Kris Kowal

--
You received this message because you are subscribed to the Google Groups "CommonJS" group.
To post to this group, send email to comm...@googlegroups.com.
To unsubscribe from this group, send email to commonjs+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/commonjs?hl=en.

Hannes Wallnoefer

unread,
May 2, 2010, 3:44:17 AM5/2/10
to comm...@googlegroups.com
2010/5/2 Kris Kowal <kris....@cixar.com>:
>
> Let's reserve the name "common/promise" for a module with an API
> similar to Promises/B but build on Promises/A thenables.
>
> Furthermore, let's reserve the name "common/q" for a a module based on
> Promises/B exactly.
>
> Let it be known that "common/q" does not interoperate with thenables,
> and that "common/promise" is strictly more, shall we say, "inclusive".

Wouldn't it be possible to create a module that implements Promises/B
(or part of it) _exactly_ on top of Primises/A thenables? Your
proposal increases API surface and provides two ways of doing the same
thing, which we should avoid IMO.

Generally, I find Promises/A too minimalistic and Promises/B too
extensive. I'm in favour of keeping initial specs as small as possible
(but large enough so that they are useful), and let implementations
add the things they deem necessary on top. I'm convinced we would then
get an iterative spec process that would be faster than trying to get
everything speced the first time around.

I've recently added a ringo/promise module as companion to async JSGI
in RingoJS:

http://ringojs.org/api/master/ringo/promise (API)
http://github.com/ringo/ringojs/blob/master/modules/ringo/promise.js (impl)

It's based on Promises/A thenables and provides a mechanism to create
and resolve promises, which I think needs to be part of the promise
API spec.

It also adds a wait() function to promises to synchronously wait for
the promise result, which makes promises usable in synchronous
contexts (e.g. making async JSGI work in servlet containers that don't
support async operation). I think this would be a worthy addition to
any promise spec as an optional operation for platforms that can
support it, but I'm fine with having it as a non-standard extension.

I would like to see promise specs converge on something like this -
the simplest possible API that provides basic promises for both the
consumer and producer side. Whether it's duck typed then or opaque
when is just an implementation detail. While i do prefer then()
because there's less magic involved, I think i could live with both.

Hannes

Irakli Gozalishvili

unread,
May 2, 2010, 8:08:59 PM5/2/10
to comm...@googlegroups.com
Kris if I got it right A supporters don't want to compromise `then` chaining but otherwise there is not big conflict. I do think that implementing `then`- ables can be possible on top of B if we'll forget about religion issues. Would it be possible to define more minimalistic B api and build A on top ?
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/
Phone: +31 614 205275
Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands

Kris Kowal

unread,
May 2, 2010, 8:35:16 PM5/2/10
to comm...@googlegroups.com
On Sun, May 2, 2010 at 5:08 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
> Kris if I got it right A supporters don't want to compromise `then` chaining
> but otherwise there is not big conflict. I do think that implementing
> `then`- ables can be possible on top of B if we'll forget about religion
> issues. Would it be possible to define more minimalistic B api and build A
> on top ?

There exists a subset of the Promises/B API that is possible to
implement with thenables from Promises/A. It is my proposal that this
idea be pursued with a specification for the "common/promise" module.

The reason I am unwilling to pursue this approach myself is that,
because Promises/A specifies that "thenables" can be produced and
consumed by external APIs, the promise module specification cannot
evolve independent of the implementation. While I encourage the
approach of agreeing upon a subset of the promise API, I do not agree
with doing so in such a way that cripples the possibility of
reintroducing the complete API as a library or extension. I do agree
that we should ratify the API, even if just a subset of the API
proposed in Promises/B (which almost exactly matches the API that
Tyler Close has implemented and exercised with Waterken).

My opinion in these matters is not likely to change (although I always
listen to technical arguments, and I never know anything for sure on
principle), but it is clear that the majority here and in the wide
world would like to or at least have committed to pursue "thenables".
While it is not ideal to fork, it may be too early to converge. I'm
proposing that we draft standards for both approaches and see what
happens. It should be possible for people to switch between
"common/promise" and "common/q" with minimal effort.

Kris Zyp

unread,
May 3, 2010, 10:29:44 AM5/3/10
to comm...@googlegroups.com


On 5/2/2010 1:44 AM, Hannes Wallnoefer wrote:
> 2010/5/2 Kris Kowal <kris....@cixar.com>:
>
>> Let's reserve the name "common/promise" for a module with an API
>> similar to Promises/B but build on Promises/A thenables.
>>
>> Furthermore, let's reserve the name "common/q" for a a module based on
>> Promises/B exactly.
>>
>> Let it be known that "common/q" does not interoperate with thenables,
>> and that "common/promise" is strictly more, shall we say, "inclusive".
>>
>
Sounds good. However, I would point out the elegance of Promise/A is
that we don't even need a CommonJS defined, platform provided, system
module for promises. We can reserve "common/promise" as a module that
should be defined with the Promise/B API, that is fine with me, but a
platform doesn't need to provide it for promises to be used. Promise/A
allows promises libraries to implemented and used by anyone, on top of
and independent of the platform, without any burden on the platform
implementor. For example, in a sense, Node is perfectly compatible with
Promise/A, as it does not produce any incompatible promises, and one
should be able use any promise library on top of it without requiring
Ryan to lift a finger. It is my belief that CommonJS should attempt to
achieve the goal of interoperability while defining as little as
possible, giving the maximum amount of freedom for innovation and
invention for implementers, and allowing independent evolution and
versioning. I believe this has been demonstrated by the fact that
independent interoperable promises implementations have been produced by
myself, Hannes, and Kowal's "events" module also produced compatible
promises, and we are successfully using promises on Node right now.
> Wouldn't it be possible to create a module that implements Promises/B
> (or part of it) _exactly_ on top of Primises/A thenables? Your
> proposal increases API surface and provides two ways of doing the same
> thing, which we should avoid IMO.
>
> Generally, I find Promises/A too minimalistic and Promises/B too
> extensive. I'm in favour of keeping initial specs as small as possible
> (but large enough so that they are useful), and let implementations
> add the things they deem necessary on top. I'm convinced we would then
> get an iterative spec process that would be faster than trying to get
> everything speced the first time around.
>

I am also definitely in favor of keeping specs as small as possible
while serving their purpose, so I am curious what you mean by Promise/A
being too minimalistic? There are plenty of interesting things that
implementers can define and add like promises, like cancellation,
timeouts, promise/deferred separation (promise creation is completely up
to implementers), and so on, and this spec leaves the doors open for all
of this, AFAICT (only closing the door on completely opaque promises).

Actually, I have been thinking that perhaps Promise/A could and maybe
should be even more minimized. I was wondering if we should eliminate
specifying what .then() returns, only specifying that it should call the
callback or error handler. This makes it even easier to create promises,
and would lighten the spec. I think this might possibly help provide
convergence with Kowal's vision of promises always being handled
indirectly through a library, (since it is closer to opaque, although
certainly not completely opaque), and would encourage using libraries
for promises. Of course anyone who wants to interact directly with the
promise (through then() calls) would presumably use a library that does
then() chaining. I know I would.

--
Thanks,
Kris

Kris Kowal

unread,
May 3, 2010, 2:37:44 PM5/3/10
to comm...@googlegroups.com
On Mon, May 3, 2010 at 7:29 AM, Kris Zyp <kri...@gmail.com> wrote:
> Promise/A allows promises libraries to implemented and used by
> anyone, on top of and independent of the platform, without any
> burden on the platform implementor. For example, in a sense, Node is
> perfectly compatible with Promise/A, as it does not produce any
> incompatible promises, and one should be able use any promise
> library on top of it without requiring Ryan to lift a finger.

This is true of Promises/B also, since Node doesn't provide any
promises at all. I have verified that the approach works well on
Node and Rhino, on top of your event loop work.

> There are plenty of interesting things that implementers can define
> and add like promises, like cancellation, timeouts, promise/deferred
> separation (promise creation is completely up to implementers), and
> so on, and this spec leaves the doors open for all of this, AFAICT
> (only closing the door on completely opaque promises).

And Promises/B supports all of these possible extensions but does not
close the door on "catchalls" that are necessary for promises to
either forward or reject missing methods. This could be accomplished
in various ways, not all of which are Promises/B, but all of which are
compatible with Promises/B, given that we do not define which approach
is being used.

1.) A promise, instead of being a "thenable", could be an "emittable",
an object with an "emit(eventName, ...args)" method. The "emit"
method could then forward to a "then(...args)" method for the "then"
event. With this approach, the "emit" method can intercept missing
methods. This approach has the price of making objects with "emit"
properties indistinguishable from promises, in much the same way that
Promises/A does not distinguish objects with "then" properties. This
means that a promise cannot be resolved to a value with an "emit" or
"then" property.

1.a.) A promise could be an instance of a hidden or exposed prototype,
discoverable by require("common/promise").isPromise(object), in which
case all promises would be distinguishable from all other objects.

2.) A promise could be a Proxy as proposed for ES-Harmony, in which
case it could be a "thenable", with a meta "get" method that can
intercept non-supported methods and either forward rejections or
forward to remote promises.

3.) A promise could be a Function(eventName, ...args), ergo backward
compatible with Tyler Close's promises. This approach has the price
of making functions indistinguishable from promises, thus a promise
cannot be resolved to a function value.

Note that "thenables" are not sufficient for the catchall requirement
in any shape or form. The closest compatible notion is emittables
(probably "emissibles" if a dictionary writer were present).

> Actually, I have been thinking that perhaps Promise/A could and
> maybe should be even more minimized. I was wondering if we should
> eliminate specifying what .then() returns, only specifying that it
> should call the callback or error handler. This makes it even easier
> to create promises, and would lighten the spec. I think this might
> possibly help provide convergence with Kowal's vision of promises
> always being handled indirectly through a library, (since it is
> closer to opaque, although certainly not completely opaque), and
> would encourage using libraries for promises. Of course anyone who
> wants to interact directly with the promise (through then() calls)
> would presumably use a library that does then() chaining. I know I
> would.

This would certainly encourage the use of a library for the
consumption of promises, if not the production. Seems like 6.5 on one
hand half a baker's dozen on the other, and the decision should rest
entirely on the shoulders of people who would be interested in
producing and consuming these promises directly.

Kris Kowal
Reply all
Reply to author
Forward
0 new messages