http://wiki.commonjs.org/wiki/Promises/B
Kris Kowal
Sorry, but I am -1 to opaque promise objects.
I am also -1 for a CommonJS spec for a complicated API that could can be
built on top of a clean simple API (with pure JS). But it makes a great
API for a library, I'm +1 for this being developed and shared in the
marketplace of asynchronous libraries. Thanks for putting the time in to
this, sorry I can't support it.
--
Thanks,
Kris
--
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.
On 3/26/2010 6:58 AM, Irakli Gozalishvili wrote:
> I don't think my opinion matters much but, I've played with both
> implementations `ref-send` and `promise` from narwhal and can't really
> see anything in `promise` that can't be build on top of `ref-send`.
> Please correct me if I'm wrong since I'm quite new to promises. If I'm
> right probably it's a good idea to ratify `B` and some platforms might
> add sugar from `A` on top of `B`, but that will be entirely new
> discussion.
>
> Playing with promises I have came to a conclusion that biggest issue
> with it's adoption is that by default (in browser) you have callbacks
> all over the place, so you kind of get used to them. I was thinking
> yesterday that basing all the async API's on top of Promises can
> change a picture, and community around it wil benefit a lot. On the
> other hand I can see that in terms of memory usage and speed lost on
> turn balancing can be any issue, I was wondering if anyone has some
> numbers on this ??
>
> Excuse me if sound crazy, since I did not slept much last night
This is a little confusing. The opening bullet point in proposal B about
opaque objects specifically mandates that proposal A can not and must
not be built on top of B or vice versa. The idea of being able to build
one library on top of the other only makes sense for building B (minus
the opaque object mandate) on top of A. This is one key reason why
proposal A makes more sense, we can build the API from B (and numerous
others) on top of it.
--
Thanks,
Kris
Alas, no, because A cannot support catch-alls. For B to work, promises
have to be able to intercept missing messages. Suppose that proxies
become available in future-ES. We would then have an opportunity for
this definition of a promise:
A promise is a proxy that receives messages. Particularly, the
"then" message is a method that accepts a callback and an optional
errback and returns a new promise. Other messages may be defined by
libraries. All messages must be handled, albeit with rejection.
That would be fine (and I would support it), apart from not permitting
a promise library to encourage use of the Q API, which is what people
*should* be doing because using the Q API prevents interleaving
hazards, regardless of how promises are implemented.
There are two ways to implement promises in the interim that would be
able to forward or reject missing methods.
One of which is to take a page from Tyler Close and make promises
functions that accept the message name as the first argument and
successive arguments variadically. With this solution, the promise
system can even wrap a message handler descriptor object and a
fallback handler function so it's every bit as simple to create
promises as the solution proposed in A, just with a facility of the Q
API.
Promise({"then": function (callback, errback) {return resolution}})
// this can be simplified to just receiving the errback since all
methods of a promise
// can opt to return the resolution so the promise machinery can
handle the callback
// and the cases where it has or has not been provided
The other of which is to define promises as objects with an "emit"
function that dispatches messages to other methods of the same object,
like "then".
{
"then": function (callback, errback) {
},
"emit": function (name) {
return this[name].apply(this,
Array.prototype.slice.call(arguments, 1));
}
}
So, sending a message to a promise is a matter of sending the message
to the "emit" method so it can route the message to the appropriate
method or forward it. This approach can be simplified by making the
"emit" method part of the prototype for a Promise object. Duck typing
would thus be facilitated by checking for the emit method, and strong
typing by checking for the Promise base.
new Promise({
"then": function (errback) {
}
})
Promise.prototype.emit = function (name, callback) {
var args = Array.prototype.slice.call(arguments, 2);
if (!this[name])
return this.proxy.apply(this, arguments);
var value = this[name].apply(this, args);
if (callback)
return callback(value);
return value;
}
Promise.prototype.proxy = function () {
return // rejected promise by default. can be overridden
}
So, the trouble we have is that there two legitimate ways to implement
promises, and one that is desirable for simplicity but not extensible.
Mandating the promises created and consumed by users with the Q API,
and permitting promises to be implemented in any of these ways (and
perhaps even handling the A case gracefully) seems to be the only way
forward without putting us in a position of regret two years down the
road, and time flies. Imagine being stuck with attachEvent for ten
years.
And really, if you take "when", "asap", "Deferred", and "Rejection" in
isolation, you've got all the methods people are going to use in
single-worker event-loops anyway but still have room to grow.
Kris Kowal
http://wiki.commonjs.org/wiki/Promises/C
With the addition of "Promise" and "Method", the rest of the library
could be safely constructed externally.
Kris Kowal
On Fri, Mar 26, 2010 at 3:27 PM, Tom Van Cutsem <to...@google.com> wrote:
> Hi Kris,
> I read your promises/B spec. Some comments:
> 1. when: shouldn't 2. "Arrange for errback to be called" also include "in a
> future turn of the event loop"?
Yes, thanks for catching that omission.
> 2. enqueue: wouldn't it be useful if 'enqueue' returned a promise for the
> result of the eventually called function?
Yes. The reason this did not occur to me is that, in my experiments
with this ref_send, I was able to factor it so that "enqueue" as
specified here was the only method I needed from lower architecture
layers, so I would import it from an event-loop module or construct it
from setTimeout and then export it directly. Since it's trivial to
produce a promise-resolving version of enqueue based on the simpler
enqueue and Deferred, producing a strict superset of the existing
enqueue, it would be simple enough to build this up. However, it
might produce unnecessary garbage collector churn.
> The intended use case for 'enqueue' seems to be to pass it an anonymous
> zero-argument function.
> A more general way to accomodate eventual execution of functions would be to
> adapt 'post' in the following way (MarkM deserves credit for this idea):
> post(object, name, args)
> if name === null, treat the invocation as a function application
> Given this semantics, 'enqueue(function () { ... })' becomes
> 'post(function() {...}, null, [])'
> I'm not sure whether this could be abbreviated to simply 'post(function() {
> ... })'?
> The advantage over 'enqueue' is that 'post' also supports functions taking
> arguments and the function's return value is propagated via the promise
> returned by 'post'
This would already be supported by post(f, "call", args) just as an
emergent pattern of the language, assuming that promises were
distinguishable from functions, which in the ref_send implementation,
they are not. In any case, I would prefer not to conflate the
responsibilities of separable functions.
Which brings up an interesting point. The present specification
implies that all values must be valid resolutions of promises. I
think this is a good thing, but it does preclude the possibility of
using the ref_send function=promise style.
> 3. Promise(descriptor, fallback): why is it that the 'when' property of the
> descriptor object only takes an errback and not a callback?
> (and similarly for the promises created by Rejection and Reference)
When I was tearing away at ref_send to grok its essence, I discovered
that *all* of the message handlers of a promise checked whether a
callback had been provided and, based on that discovery, decided
whether to forward the resolution to the callback, or to return the
resolution.
http://github.com/kriskowal/narwhal/blob/ref-send-experiment/lib/ref-send.js#L106
So, it was possible to simplify the promise protocol such that they
always *returned* the resolution and the promise would route it either
forward or backward.
http://github.com/kriskowal/narwhal/blob/ref-send-experiment/lib/ref-send.js#L160
> 4. Deferred(): would it make sense to define another operation
> "reject(reason)" on the return value of Deferred()?
> This would be a shorthand for "resolve(Rejection(reason))". However, I
> usually think of either resolving or rejecting a promise, so it feels
> strange having to express rejecting a promise by "resolving" it.
It is odd and I've thought about it. The reason I've hesitated to do
that in this incarnation is that it is futile to avoid the idea that
rejection is the same as resolution with a rejection without
drastically complicating the API. The promise protocol requires you
to return a rejection on failure, which then gets forwarded to the
callback, which is often merely a resolve function. When it comes
down to it, using the resolver exclusively often simplifies the
code-paths to a single channel for both resolution and rejection.
Having a reject method invites unnecessarily complicated code, and
only does so when using the Deferred function. The interesting thing
about the Deferred function is that, in the long run, the only places
where you need to use Deferred directly are the ones where your'e
constructing a promise API based on lower-level callback API. It's my
projection that Deferred will not get much use outside of frameworks.
> 5. Reference: shouldn't 3. be put(name,value) rather than put(name)? Also,
> is it intentional that the resolved promise has no 'post' method?
Both omissions. Thanks.
> 6. AFAICT, the current API has no reliable way of testing whether a value is
> a promise. What about adding an "isPromise(object)" predicate?
Can do. Here are all of the fixes:
http://wiki.commonjs.org/index.php?title=Promises%2FB&diff=2534&oldid=2532
Kris Kowal
The purpose of an abstraction is to allow us to better model our
problems, giving us the ability to build more complex programs. In my
experimentation with promises thus far, they are failing to give me
any more advantage in managing complexity than callbacks within
closures.
I've watched the video (http://vimeo.com/9149445), read and
reimplemented Kris Kowal's events module from narwhal, as well as seen
Doug Crockfords "execution vat" theories, but I'm still unable to
grasp anything concrete in my own use of these APIs.
Is there anybody that can show a concrete example of how promises will
allow me to manage the complexity of my programs better?