A Different Approach to Future/Promise

353 views
Skip to first unread message

David Nolen

unread,
Mar 24, 2010, 1:15:37 PM3/24/10
to CommonJS
I'm starting to following the development around promises in
JavaScript-land and in the CommonJS Spec. I've looked at the various
approaches and I've found them greatly wanting. They either:

1) Involve explicit callbacks, or
2) Force the user to put what I consider to be implementation details
_into_ their functions

I've been working on a library called Promises.js for about 9 months
now and I adopt a very different approach. If you just want to see it
before hearing a description look here: http://mootools.net/shell/q9ASd/28/

Promise objects themselves encapsulate all the plumbing. The only
thing that needs to be done is for the user to which declare functions
*might* execute in the future.

var add = function(a, b) {
return a + b;
}.future();

var a = new Promise();
var b = new Promise();
var sum = add(a, b);
sum.value(); // undefined
a.deliver(4);
sum.value(); // undefined
b.deliver(5);
sum.value(); // 9

You never wait for promises. You simply send them to functions which
can handle them. Watching a promise is implicit. Any function
decorated in this manner will automatically detect if a unrealized
promise crossed it's function interface and forces it to realize. It
also returns a promise for it's result. This promise can be passed to
other functions decorated with future.

The end result is that code that looks like the following is fully
asynchronous yet written in a much more natural style:

var resources = ['a', 'b', 'c', 'd', 'e', 'f'];
var result = "";
while(resource = resources.shift()) {
result = add(result, get(resource)); // get is an asynchronous
request
}
show(result, 'ex2'); // -> abcdef

You can get the source here: http://github.com/ShiftSpace/promises

I think this approach is much more natural. The only interface promise-
wrappable objects need to implement is something like "realize" and
"onRealize". Feedback appreciated.

I'm putting this out there not to talk about specifications but to
highlight a different approach/philosophy.

David

Kris Zyp

unread,
Mar 24, 2010, 4:03:46 PM3/24/10
to comm...@googlegroups.com, David Nolen

Your function decoration is indeed clever. However, it is certainly not
at odds with CommonJS proposed promises. Using the "promise" module from
narwhal or node-promise, I think you could actually implement the
decorator in a few lines:

var all = require("promise").all;
Function.prototype.future = function(func){
return function(){
var args = Array.prototype.slice.call(arguments),
self = this;
return all(args).then(function(resolvedArgs){
return func.apply(self, resolvedArgs);
});
};
};

Anyway, I think it very cool idea, great job!

--
Thanks,
Kris

Irakli Gozalishvili

unread,
Mar 25, 2010, 5:44:40 PM3/25/10
to comm...@googlegroups.com
@david

I really like this API, but think extending natives won't be widely accepted on this mailing list.
On the other hand I think we can have something very similar but slightly different.

var add = Future(function(a, b) {
 return a + b;
});

@kris

I'm trying to understand your snippet but can't really get what is `func`

BTW I would really be happy to move on with Promises!!

--
Irakli Gozalishvili
Web: http://rfobic.wordpress.com/
Phone: +31 614 205275
Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands



--
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.


Kris Kowal

unread,
Mar 25, 2010, 5:50:27 PM3/25/10
to comm...@googlegroups.com
On Thu, Mar 25, 2010 at 2:44 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
> BTW I would really be happy to move on with Promises!!

I'd love to move "forward" with promises as well. Unfortunately, I
think ".then" has to go, for reasons explained by Tyler Close, which
is very "sad panda" because I know a lot of us have our hearts set on
it. Which is to say, we have more arguing to do about promises.

Kris Kowal

Kris Zyp

unread,
Mar 25, 2010, 6:18:37 PM3/25/10
to comm...@googlegroups.com

On 3/25/2010 3:44 PM, Irakli Gozalishvili wrote:
> @david
>
> I really like this API, but think extending natives won't be widely
> accepted on this mailing list.
> On the other hand I think we can have something very similar but
> slightly different.
>
> var add = Future(function(a, b) {
> return a + b;
> });

Right, this can be implemented as standalone pure JavaScript library, so
it doesn't need a CommonJS spec.


>
> @kris
>
> I'm trying to understand your snippet but can't really get what is `func`
>
> BTW I would really be happy to move on with Promises!!
>

I think we have eliminated the notion of ratification in CommonJS, but
the core of the spec (the then() method) of the promise proposal has
existed in basically the same form for almost a year now, it has
implementations in Narwhal, node-promise, Dojo, and maybe Qooxdoo and/or
EJScript?. Outside of modules and packages, I believe this is one
CommonJS's most broadly accepted and implemented specs. I guess we could
add a version number, would that be helpful?

--
Thanks,
Kris

Kris Kowal

unread,
Mar 25, 2010, 6:25:37 PM3/25/10
to comm...@googlegroups.com
On Thu, Mar 25, 2010 at 3:18 PM, Kris Zyp <kri...@gmail.com> wrote:
> I think we have eliminated the notion of ratification in CommonJS, but
> the core of the spec (the then() method) of the promise proposal has
> existed in basically the same form for almost a year now, it has
> implementations in Narwhal, node-promise, Dojo, and maybe Qooxdoo and/or
> EJScript?. Outside of modules and packages, I believe this is one
> CommonJS's most broadly accepted and implemented specs. I guess we could
> add a version number, would that be helpful?

Kris Zyp,

I know you want to move forward with this, but I decidedly would like
to move backward. I would like to work with you toward removing
"then" from Narwhal's promises.

Kris Kowal

Kris Zyp

unread,
Mar 25, 2010, 7:49:28 PM3/25/10
to comm...@googlegroups.com

Do you have a link to this conversation? I thought we had clearly
pointed out how the objections to ".then" are no longer relevant. But
regardless, CommonJS is primarily about proposals, discussion, and
implementing. Obviously you are free to make a new proposal if you want,
just like any other spec area. Of course that won't undo the status of
the current proposal as having a long stable history and several
implementations and the resulting effort it would take to convince
everyone to implement something else. But please feel free to propose
something else, new ideas are always welcome. I'd be definitely be
surprised if you have something new that would convince me to change any
of the implementations that I am responsible for, but this an open
community, go for it!

--
Thanks,
Kris

Kris Kowal

unread,
Mar 25, 2010, 8:09:03 PM3/25/10
to comm...@googlegroups.com
On Thu, Mar 25, 2010 at 4:49 PM, Kris Zyp <kri...@gmail.com> wrote:
> Do you have a link to this conversation? I thought we had clearly
> pointed out how the objections to ".then" are no longer relevant.

http://groups.google.com/group/commonjs/browse_thread/thread/d567d30e2bbea598

We established that Tyler's Close's ref_send does not prevent a user
from making the mistake of forwarding "then" directly. We did not
establish that it would be impossible to construct an implementation
that did prevent this error.

We also established that the "then" protocol would not support
catch-alls for erroneous, unsupported, or proxied messages other than
"then", like "post". I am not convinced that we should preclude the
possibility of using never-resolved objects for communicating messages
to objects in other workers, which is the error that the "then"
protocol amounts to. I proposed that promises could be opaque objects
operated on by the Q API so that we would have the flexibility of
upgrading them or tuning them for various implementations. Tyler
dropped the ball on writing a proposal. I wrote up a list of
requirements at the bottom of the promise topic page [1]. I'll pick
up the ball for Tyler.

I hope that your prototypes are not so committed that you would be
unwilling to fix these shortcomings for the Persevere community; I do
not think that it is so far gone that we can not escape the problem
with a deprecation and migration path. It would not be necessary for
the implementations to ditch ".then"; it would be necessary for the
specification to make the Q API the exclusive CommonJS certified way
of producing and consuming promises so that some implementations would
have the luxury of not building the ".then" bug into their promises.

Kris Kowal

.. [1] http://wiki.commonjs.org/wiki/Promises

Daniel Friesen

unread,
Mar 25, 2010, 10:04:04 PM3/25/10
to comm...@googlegroups.com
Kris Kowal wrote:
> ...

> We also established that the "then" protocol would not support
> catch-alls for erroneous, unsupported, or proxied messages other than
> "then", like "post". I am not convinced that we should preclude the
> possibility of using never-resolved objects for communicating messages
> to objects in other workers, which is the error that the "then"
> protocol amounts to. I proposed that promises could be opaque objects
> operated on by the Q API so that we would have the flexibility of
> upgrading them or tuning them for various implementations. Tyler
> dropped the ball on writing a proposal. I wrote up a list of
> requirements at the bottom of the promise topic page [1]. I'll pick
> up the ball for Tyler.
> ...Ohh, passing promises to other workers... I'll add that to my list of
cross-worker communication techniques...

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Kris Zyp

unread,
Mar 25, 2010, 11:18:28 PM3/25/10
to comm...@googlegroups.com

On 3/25/2010 6:09 PM, Kris Kowal wrote:
> On Thu, Mar 25, 2010 at 4:49 PM, Kris Zyp <kri...@gmail.com> wrote:
>
>> Do you have a link to this conversation? I thought we had clearly
>> pointed out how the objections to ".then" are no longer relevant.
>>
> http://groups.google.com/group/commonjs/browse_thread/thread/d567d30e2bbea598
>
> We established that Tyler's Close's ref_send does not prevent a user
> from making the mistake of forwarding "then" directly. We did not
> establish that it would be impossible to construct an implementation
> that did prevent this error.
>
> We also established that the "then" protocol would not support
> catch-alls for erroneous, unsupported, or proxied messages other than
> "then", like "post". I am not convinced that we should preclude the
> possibility of using never-resolved objects for communicating messages
> to objects in other workers, which is the error that the "then"
> protocol amounts to. I proposed that promises could be opaque objects
> operated on by the Q API so that we would have the flexibility of
> upgrading them or tuning them for various implementations. Tyler
> dropped the ball on writing a proposal. I wrote up a list of
> requirements at the bottom of the promise topic page [1]. I'll pick
> up the ball for Tyler.
>

These are the same arguments. Adding methods to promises for handling
posts, puts, and anything else is easy enough. The benefits of trying to
prevent direct promise usage by forcing users to use wrappers that
preserve turn and exception handling invariants (when it doesn't
preclude the use of such invariants preserving wrappers like Q.when)
that only benefits users for the slim intersection of untrusted promises
interacting with turn-sensitive side-effect inducing or non-exception
ready code isn't even close to as compelling as the convenience,
brevity, and readability of giving users the option of directly
interacting with clean well-specified promises.

--

Thanks,
Kris

Kris Kowal

unread,
Mar 25, 2010, 11:55:07 PM3/25/10
to comm...@googlegroups.com
On Thu, Mar 25, 2010 at 8:18 PM, Kris Zyp <kri...@gmail.com> wrote:

> These are the same arguments. Adding methods to promises for handling
> posts, puts, and anything else is easy enough.

But catch-alls are not; the atom of a promise would have to be a
method that accepts the message name and directs to the appropriate
handler.

> The benefits of trying to
> prevent direct promise usage by forcing users to use wrappers that
> preserve turn and exception handling invariants (when it doesn't
> preclude the use of such invariants preserving wrappers like Q.when)
> that only benefits users for the slim intersection of untrusted promises
> interacting with turn-sensitive side-effect inducing or non-exception
> ready code isn't even close to as compelling as the convenience,
> brevity, and readability of giving users the option of directly
> interacting with clean well-specified promises.

Unfortunately it suffices to say this this is subjective and I
disagree. I hope that the proposal I've posted for Promises/B at
least dissolves your claim that the alternative is not well specified.

Kris Kowal

Dean Landolt

unread,
Mar 26, 2010, 12:41:16 AM3/26/10
to comm...@googlegroups.com
On Thu, Mar 25, 2010 at 11:55 PM, Kris Kowal <cowber...@gmail.com> wrote:
On Thu, Mar 25, 2010 at 8:18 PM, Kris Zyp <kri...@gmail.com> wrote:

> These are the same arguments. Adding methods to promises for handling
> posts, puts, and anything else is easy enough.

But catch-alls are not; the atom of a promise would have to be a
method that accepts the message name and directs to the appropriate
handler.

Can you clarify what you mean by this? Are you concerned that hanging `then` (or any key) on the returned promise object would pollute it and preclude a clean implementation of catchalls (whenever the come to es)? If so, how would you recommend we duck-test for promises?
 

> The benefits of trying to
> prevent direct promise usage by forcing users to use wrappers that
> preserve turn and exception handling invariants (when it doesn't
> preclude the use of such invariants preserving wrappers like Q.when)
> that only benefits users for the slim intersection of untrusted promises
> interacting with turn-sensitive side-effect inducing or non-exception
> ready code isn't even close to as compelling as the convenience,
> brevity, and readability of giving users the option of directly
> interacting with clean well-specified promises.

Unfortunately it suffices to say this this is subjective and I
disagree.  I hope that the proposal I've posted for Promises/B at
least dissolves your claim that the alternative is not well specified.

Kris Kowal

Kevin Dangoor

unread,
Mar 26, 2010, 7:26:31 AM3/26/10
to comm...@googlegroups.com
On Wed, Mar 24, 2010 at 1:15 PM, David Nolen <dnolen...@gmail.com> wrote:
Promise objects themselves encapsulate all the plumbing. The only
thing that needs to be done is for the user to which declare functions
*might* execute in the future.

var add = function(a, b) {
 return a + b;
}.future();

var a = new Promise();
var b = new Promise();
var sum = add(a, b);
sum.value(); // undefined
a.deliver(4);
sum.value(); // undefined
b.deliver(5);
sum.value(); // 9

You never wait for promises. You simply send them to functions which
can handle them. Watching a promise is implicit. Any function
decorated in this manner will automatically detect if a unrealized
promise crossed it's function interface and forces it to realize. It
also returns a promise for it's result. This promise can be passed to
other functions decorated with future.

What happens to exceptions thrown within the asynchronous code?

This is really quite cool, but I worry about error handling.

Kevin


--
Kevin Dangoor

work: http://labs.mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com

David Nolen

unread,
Mar 26, 2010, 9:51:48 AM3/26/10
to comm...@googlegroups.com
On Fri, Mar 26, 2010 at 7:26 AM, Kevin Dangoor <dan...@gmail.com> wrote:

What happens to exceptions thrown within the asynchronous code?

This is really quite cool, but I worry about error handling.

Kevin

In my own code I have a special function which simulates "if" as well as one for detecting errors.

_if(_noErr(p),
     function(v) { ... true branch ... },
     function(v) { ... false branch ... });

I also made my implementation store basic error data in the Promise itself. In general this has worked for me in a codebase that has 25k+ LOC of JavaScript. I'm sure a lot of more clever solutions then mine could be arrived at.

David
Reply all
Reply to author
Forward
0 new messages