Futures
Tab Atkins Jr. wrote:
This group is public-script-coord, which we're already having the discussion on, so... success!
THE SYSTEM IS WORKING!
Sorry for catch-up replies, I will try to stifle. ;-)
Anne van Kesteren wrote:
On Thu, Apr 18, 2013 at 2:00 PM, Jorge<jorge at jorgechamorro.com> wrote:
You guys ought to be deeply embarrassed because HTML5 is not your child.
I don't even
www.whatwg.org, remember? brendaneich.com/2004/06/the-non-world-non-wide-non-web, en.wikipedia.org/wiki/Web_Hypertext_Application_Technology_Working_Group and all that :-P.
Bygones? Sure.
But fool me twice still applies, so: trust but verify.
Really, we can have good work happen anywhere with the right patent protocols. Ecma TC39 wants Promises, DOM folks want Futures. Can't we all just get alone? I think so.
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation.
Le 20/04/2013 15:17, Brendan Eich a ?crit :
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation.
I find pretty interesting cases where high-level expressive syntax results in optimizations that were impossible to very hard without the syntax. Is it already implemented in SpiderMonkey? If not, is there a bug number to follow the progress?
Thanks,
David
David Bruant wrote:
Le 20/04/2013 15:17, Brendan Eich a écrit :
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation. I find pretty interesting cases where high-level expressive syntax results in optimizations that were impossible to very hard without the syntax. Is it already implemented in SpiderMonkey? If not, is there a bug number to follow the progress?
Implemented long ago:
js> function cons(h, t) { return {head: h, tail: t}; }
js> let {head, tail} = cons("hi", "bye")
js> head "hi" js> tail "bye"
(Not optimized yet, AFAIK. Care to file that bug? Cc: me if you do. Thanks!)
Brendan Eich wrote:
Can't we all just get alone?
Oh, the irony! Typo, not snark :-).
On Sat, Apr 20, 2013 at 6:17 AM, Brendan Eich <brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation.
Yeah, that's fine when you're explicitly invoking something that needs to return two objects.
It's less fine when you have something like a hypothetical getJSON() (future-returning XHR sugar), which can reasonably just return a future, but which we also want to make cancellable. Burdening every call site with the need to pull out the future from the returned object isn't great. It also makes it hard to upgrade a call from future-returning to cancellable-future-returning - you're pretty much forced to use a variant function name. (In the case of cancellable futures, we might be able to do it with an additional argument to the relevant functions, instead.)
Lisp's multiple return values were nice in that regard - naive call sites, or simply ones that only needed the primary values - could just call it like a normal function that returned a single value. Cluefull call sites that did need the additional return values just used values-list or multiple-value-bind to get all of the values.
This is probably just an idle wish, though, as destructuring takes a lot of the wind out of the sails of multiple values. :/
On Sat, Apr 20, 2013 at 1:54 PM, Brendan Eich <brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
This group is public-script-coord, which we're already having the discussion on, so... success!
THE SYSTEM IS WORKING!
Sorry for catch-up replies, I will try to stifle. ;-)
For what it's worth, I'd love to see TC39 take on Promises. If they are called "Future" or not matters less. I pushed for DOMEvents to be dropped from Futures with one of the goals being to make them less DOM dependent and more possible to spec in TC39 (I had other more important goals too, I'm not particularly a fan of DOM Events).
In particular, I'd love to get TC39 to look over the "is-a-future" issue. I'm pretty worried about the current solution which makes "then" a magic property name. It's less bad than "proto" is, but not by a lot.
However I also think it's critical that we get promises/Futures soon. We've been writing specs for a long time which suffers from the lack of them. We're probably permanently adding APIs to the web platform on a monthly basis which would be significantly improved if we used promises. Waiting until ES7 is simply not an option.
I don't have any solutions to propose. But I'd be happy to see Futures removed from the DOM spec once there is a TC39 draft that we can point to and start implementing.
/ Jonas
In particular, I'd love to get TC39 to look over the "is-a-future" issue. I'm pretty worried about the current solution which makes "then" a magic property name. It's less bad than "proto" is, but not by a lot.
I agree - I'm putting together a list of a few issues with Futures that I would like to see addressed, and this is one.
On Sun, Apr 21, 2013 at 4:58 AM, Kevin Smith <zenparsing at gmail.com> wrote:
In particular, I'd love to get TC39 to look over the "is-a-future" issue. I'm pretty worried about the current solution which makes "then" a magic property name. It's less bad than "proto" is, but not by a lot.
I agree - I'm putting together a list of a few issues with Futures that I would like to see addressed, and this is one.
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value.
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value.
What about using a symbol for the then
protocol? Libraries can be
upgraded to use the symbol as an alias for then
. It set up a dependency
on ES6, of course...
Le 22/04/2013 14:26, Kevin Smith a écrit :
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value.
What about using a symbol for the
then
protocol? Libraries can be upgraded to use the symbol as an alias forthen
. It set up a dependency on ES6, of course...
And it doesn't address the compatibility problem people want to address. I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries?
Eventually, what will be the point of a promise library if there is native support?
What about using a symbol for the
then
protocol? Libraries can be upgraded to use the symbol as an alias forthen
. It set up a dependency on ES6, of course...And it doesn't address the compatibility problem people want to address.
It still does I think. It's cheap for a library to add a symbol-named
alias for then
. But it's expensive for a library's clients to replace
all usage sites with a different API. Even though then
is standard, the
other parts are not.
- Q.defer
- new jQuery.Deferred
- new Future
- ...etc
On Mon, Apr 22, 2013 at 1:37 PM, David Bruant <bruant.d at gmail.com> wrote:
I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries?
They are compatible because of how futures make use of then.
Le 22/04/2013 15:07, Kevin Smith a écrit :
What about using a symbol for the `then` protocol? Libraries can be upgraded to use the symbol as an alias for `then`. It set up a dependency on ES6, of course...
And it doesn't address the compatibility problem people want to address.
It still does I think. It's cheap for a library to add a symbol-named alias for
then
. But it's expensive for a library's clients to replace all usage sites with a different API. Even thoughthen
is standard, the other parts are not.
- Q.defer
- new jQuery.Deferred
- new Future
- ...etc
I see. I guess it's pretty much as much work as wrapping a native future for a library promise and vice versa.
Le 22/04/2013 15:12, Anne van Kesteren a écrit :
On Mon, Apr 22, 2013 at 1:37 PM, David Bruant <bruant.d at gmail.com> wrote:
I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries? They are compatible because of how futures make use of then.
Were they not, how big of a burden would wrapping be? Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises?
On Mon, Apr 22, 2013 at 5:37 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 22/04/2013 14:26, Kevin Smith a écrit :
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value.
What about using a symbol for the
then
protocol? Libraries can be upgraded to use the symbol as an alias forthen
. It set up a dependency on ES6, of course...And it doesn't address the compatibility problem people want to address. I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries?
Eventually, what will be the point of a promise library if there is native support?
This is the great irony. Even if standard platform-provided ES promises win in the end, there will be a long transition period where there are several co-existing libraries. In order for standard platform-provided ES promises to win, it has to thrive in the environment of that co-existence. If it ever wins definitively, so that there are no remaining libraries of concern, it will no longer need the assimilation mechanisms that enabled that co-existence; but by then it will be too late to shed it. I would like a realistic way out of this dilemma. But after a tremendous number of messages on the promises/A+ site about exactly this issue (please read or at least skim), I don't think any of the alternatives are realistic. At < promises-aplus/promises-spec#94>
I write:
The original E promise API which inspired all these others has no
Le 22/04/2013 16:15, Mark S. Miller a écrit :
On Mon, Apr 22, 2013 at 5:37 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:
Le 22/04/2013 14:26, Kevin Smith a écrit :
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value. What about using a symbol for the `then` protocol? Libraries can be upgraded to use the symbol as an alias for `then`. It set up a dependency on ES6, of course...
And it doesn't address the compatibility problem people want to address. I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries? Eventually, what will be the point of a promise library if there is native support?
This is the great irony. Even if standard platform-provided ES promises win in the end, there will be a long transition period where there are several co-existing libraries. In order for standard platform-provided ES promises to win, it has to thrive in the environment of that co-existence.
In my opinion, integration to other platform APIs is a legitimate unfair advantage of platform promises against existing libraries. This advantage is what will make platform promises win. Especially given that both the Future standard library and its integration in other platform APIs are polyfillable. I believe we can agree that the Future standard is just the first step toward integrating more deeply in the platform and not just a built-in promise library.
Said otherwise, as soon as built-in futures are in, integrated in at least one other platform API, deployed in mainstream web browsers and an open source polyfill is available, why would a dev ever use a separate promise library?
Unless I'm missing something, platform Futures are doomed to win this battle.
If it ever wins definitively, so that there are no remaining libraries of concern, it will no longer need the assimilation mechanisms that enabled that co-existence; but by then it will be too late to shed it. I would like a realistic way out of this dilemma.
Each existing library can wrap platform futures to build one of their own. That sounds realistic enough, doesn't it? That can be enough for Futures to become the new standard. This would be pretty much the equivalent of "use DOM methods on native DOM nodes and jQuery methods on jQuery objects" DOM methods play with and return DOM objects, jQuery methods plays with and generates jQuery objects (almost) and jQuery provides a one-directional way to turn a DOM object into a jQuery object.
But after a tremendous number of messages on the promises/A+ site about exactly this issue (please read or at least skim), I don't think any of the alternatives are realistic. At promises-aplus/promises-spec#94 I write:
The original E promise API which inspired all these others has no assimilation. When I first heard about assimilation, I thought it was a terrible idea, for many of the same reasons that underlie many of the above objections. However, as I saw the JS promise landscape evolve, I despaired more over a worse problem: Several similar but different elephants in the room: jQuery promises, WinJS promises, Q promises, and DOMFutures. All of these but DOMFutures have enough installed base that they're not going away. And re DOMFutures, never underestimate the ability of the w3c to be an elephant even for premature "standards". Much code will have to be written in an environment inhabited by multiple such promise systems simultaneously. If none of these recognize each other's promises as anything promise-like, then programmers will face the burden of dealing with a "jQuery promise for a Q promise for a WinJS promise for a DOMFuture for a number". Call it the JQPFAQPFAWJPFADFFAN problem. I didn't see how we could get from that situation to one with an agreed standard promise anything.
I wonder how frequent it is to have 2 promise libraries in the same runtime and want them to interact. Which is pretty much asking whether people import 2 DOM manipulation libraries and want these libraries to cohabit. Isn't at least 80% of the case just one promise library interacting with the platform? If it is, can the library-wraps-native-promise rule be enough?
Fortunately, the starting point for all of these elephants was so similar that the Promises/A+ process was able to tease out and codify a common-enough ground for all of these to agree on. This is messy real-world standards work; as much politics and sociology as technology and math. Given the constraints imposed by legacy, we should all be overjoyed that the Promises/A+ community has been able to extract something as beautiful as they did. But even if they all agreed on exactly the same spec, so long as jQuery promises only recognize jQuery promises as promises, and likewise for the others, we would still have the JQPFAQPFAWJPFADFFAN problem. Had ES6 with its unique symbols happened long before any of these promise libraries, we could have used a unique "@then" symbol for assimilation and so avoided the accidental collision @killdream worries about above. Likewise, if all of these had stuck with the E (or original Q) name "when" rather than renaming it "then", we would have less accidental collision, and would be less irritating to category theorists. (FWIW, I think "when" was a much better term than "then" for this anyway. I am still unclear on the history of how this got renamed.) Alas, it didn't turn out that way. Given multiple promise systems that each recognize only their own promises as promises, but which mostly agree on the meaning of "then", assimilation turns out to be a surprisingly pleasant way for these to co-exist. Given the actual situation we have to start from, having this assimilation be based on duck typing on the presence of a function named "then" is unfortunate, but there was no other practical choice.
Each library can add: if(nativeFuture(p)) p = wrapNativeFuture(p)
to the beginning each of its method accepting a promise as argument (or equivalent is the promise is in 'this')
These 2 lines (+wrapNativeFuture) sound pretty practical to me for the transition period.
On Mon, Apr 22, 2013 at 10:15 AM, Mark S. Miller <erights at google.com> wrote:
On Mon, Apr 22, 2013 at 5:37 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 22/04/2013 14:26, Kevin Smith a écrit :
Thenable futures are uglier than branded futures, but also the only way to remain compatible with the various libraries that are out there today, which is something many people value.
What about using a symbol for the
then
protocol? Libraries can be upgraded to use the symbol as an alias forthen
. It set up a dependency on ES6, of course...And it doesn't address the compatibility problem people want to address. I personally wonder whether the compatibility with existing libraries is so important. It should be pretty easy to wrap a native future into a library future (aren't native futures already Promise/A+ compatible?) and vice versa. Why isn't it enough to help with compatibility with libraries?
Eventually, what will be the point of a promise library if there is native support?
This is the great irony. Even if standard platform-provided ES promises win in the end, there will be a long transition period where there are several co-existing libraries. In order for standard platform-provided ES promises to win, it has to thrive in the environment of that co-existence. If it ever wins definitively, so that there are no remaining libraries of concern, it will no longer need the assimilation mechanisms that enabled that co-existence; but by then it will be too late to shed it. I would like a realistic way out of this dilemma. But after a tremendous number of messages on the promises/A+ site about exactly this issue (please read or at least skim), I don't think any of the alternatives are realistic. At < promises-aplus/promises-spec#94> I write:
The original E promise API which inspired all these others has no
assimilation. When I first heard about assimilation, I thought it was a terrible idea, for many of the same reasons that underlie many of the above objections. However, as I saw the JS promise landscape evolve, I despaired more over a worse problem:
Several similar but different elephants in the room: jQuery promises, WinJS promises, Q promises, and DOMFutures. All of these but DOMFutures have enough installed base that they're not going away. And re DOMFutures, never underestimate the ability of the w3c to be an elephant even for premature "standards". Much code will have to be written in an environment inhabited by multiple such promise systems simultaneously. If none of these recognize each other's promises as anything promise-like, then programmers will face the burden of dealing with a "jQuery promise for a Q promise for a WinJS promise for a DOMFuture for a number". Call it the JQPFAQPFAWJPFADFFAN problem. I didn't see how we could get from that situation to one with an agreed standard promise anything.
Fortunately, the starting point for all of these elephants was so similar that the Promises/A+ process was able to tease out and codify a common-enough ground for all of these to agree on. This is messy real-world standards work; as much politics and sociology as technology and math. Given the constraints imposed by legacy, we should all be overjoyed that the Promises/A+ community has been able to extract something as beautiful as they did. But even if they all agreed on exactly the same spec, so long as jQuery promises only recognize jQuery promises as promises, and likewise for the others, we would still have the JQPFAQPFAWJPFADFFAN problem.
Had ES6 with its unique symbols happened long before any of these promise libraries, we could have used a unique "@then" symbol for assimilation and so avoided the accidental collision @killdream worries about above. Likewise, if all of these had stuck with the E (or original Q) name "when" rather than renaming it "then", we would have less accidental collision, and would be less irritating to category theorists. (FWIW, I think "when" was a much better term than "then" for this anyway. I am still unclear on the history of how this got renamed.) Alas, it didn't turn out that way.
I may be partly responsible for this -- I think I helped convince Kris
Kowal, who championed the Promises/C proposal (not sure if there was ever a
Promises/B) which only used when
that the when
function could not be
made interoperable between libraries because it had to rely solely on
branding checks. This was back before Symbols were even a thing and
proto was considered for standardization. The presence of either would
have cut my argument at the knees.
Given multiple promise systems that each recognize only their own promises
as promises, but which mostly agree on the meaning of "then", assimilation turns out to be a surprisingly pleasant way for these to co-exist. Given the actual situation we have to start from, having this assimilation be based on duck typing on the presence of a function named "then" is unfortunate, but there was no other practical choice.
Not at the time, no. When Symbols (née Names) were proposed I'd suggested on this very list that they could be employed to solve exactly this problem. At the time I guess this wasn't a problem people cared much about, but this was before jQuery, WinJS and DOMFuture promises -- back when we could have adapted to something more future-friendly -- a registry of keys for branding, perhaps? I think I'd suggested that the module system could serve as an ontology for these kinds of well-known symbols, and that we could shim this functionality today. I'm sure all the really smart folks grinding on this problem today would have had even better ideas. I'm glad everyone's come around, but alas, too late to fix this :-/
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises?
This is a really bad misconception that you have repeated several times now.
DOM Futures, and possibly ECMAScript promises, are practically feature-less. They have then
, catch
sugar, a few static combinator methods, and that's it. If you want to do anything serious with promises, you're going to need a lot more than that. Check out the extensive API Q provides, for example:
kriskowal/q/wiki/API-Reference
In particular, I and teams I have worked on in real-world projects use promise.finally
, the promise-for-object methods, the promise-for-function methods, and the utility methods literally every day. (To say nothing of the Node.js-interfacing methods.) Features like long stack traces have been invaluable for us. When.js has similar extra capabilities beyond the basics, and even RSVP (which is pretty lightweight) has RSVP.hash
for shallowly settling any promise properties of an object. And the Q ecosystem is built around different fundamental primitives from DOM Futures which allow things like promise for remote objects, promise pipelining, and the like---use cases which are increasingly important.
To think that users who are accustomed to this level of flexibility are going to suddenly switch to DOM Futures/ECMAScript promises is very naive. More likely, those will be used alongside more full-featured promises returned from other parts of the system---forever, not just in some transition period. Thus, interop is going to be necessary for an ergonomic experience.
On Mon, Apr 22, 2013 at 8:16 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises?
This is a really bad misconception that you have repeated several times now.
Hi Domenic, I just repeated it too:
If [standard platform-provided ES promises] ever wins definitively, so that there are no remaining libraries of concern, it will no longer need the assimilation mechanisms that enabled that co-existence; but by then it will be too late to shed it.
I agree that Promises/A+ are too minimal, that DOMFutures does not solve the problems that need solving, and that the Q API you link to is an excellent start. But I'm actually more hopeful that, after a transition period, a standard platform-provided ES7 promises along the lines of Q might be good enough that other library needs can be built on it rather than more directly on the underlying platform. Nevertheless, the constraints imposed by this transition period remain AFAICT inescapable, and so the conclusion -- assimilation -- remains the same.
On Mon, Apr 22, 2013 at 11:16 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises?
This is a really bad misconception that you have repeated several times now.
DOM Futures, and possibly ECMAScript promises, are practically feature-less. They have
then
,catch
sugar, a few static combinator methods, and that's it. If you want to do anything serious with promises, you're going to need a lot more than that. Check out the extensive API Q provides, for example:kriskowal/q/wiki/API-Reference
In particular, I and teams I have worked on in real-world projects use
promise.finally
, the promise-for-object methods, the promise-for-function methods, and the utility methods literally every day. (To say nothing of the Node.js-interfacing methods.) Features like long stack traces have been invaluable for us. When.js has similar extra capabilities beyond the basics, and even RSVP (which is pretty lightweight) hasRSVP.hash
for shallowly settling any promise properties of an object. And the Q ecosystem is built around different fundamental primitives from DOM Futures which allow things like promise for remote objects, promise pipelining, and the like---use cases which are increasingly important.To think that users who are accustomed to this level of flexibility are going to suddenly switch to DOM Futures/ECMAScript promises is very naive. More likely, those will be used alongside more full-featured promises returned from other parts of the system---forever, not just in some transition period. Thus, interop is going to be necessary for an ergonomic experience.
To be fair I don't think David is suggesting that these libraries won't be
used, just that the kind of promises that are produced and consumed will
coalesce in the presence of a platform standard. And in order for that
standard to solve the primary problem being discussed, then
would have to
fall by the wayside.
FWIW I disagree with him -- I strongly suspect that by the time this were to all go down and a stable polyfill existed there'd already be too much then-demanding code in the wild. There probably already is. And at that point it's proto all over again -- the standard will have no choice but to respect then and the problem cannot be fixed :-/
FWIW I disagree with him -- I strongly suspect that by the time this were to all go down and a stable polyfill existed there'd already be too much then-demanding code in the wild. There probably already is. And at that point it's proto all over again -- the standard will have no choice but to respect then and the problem cannot be fixed :-/
Why not? If the then
symbol is well-known (e.g. easily imported from
somewhere), then why can't libraries be upgraded to use it as an alias for
their then
method?
Le 22/04/2013 17:16, Domenic Denicola a écrit :
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises? This is a really bad misconception that you have repeated several times now.
DOM Futures, and possibly ECMAScript promises, are practically feature-less. They have
then
,catch
sugar, a few static combinator methods, and that's it. If you want to do anything serious with promises, you're going to need a lot more than that. Check out the extensive API Q provides, for example:kriskowal/q/wiki/API-Reference
In particular, I and teams I have worked on in real-world projects use
promise.finally
, the promise-for-object methods, the promise-for-function methods, and the utility methods literally every day. (To say nothing of the Node.js-interfacing methods.) Features like long stack traces have been invaluable for us. When.js has similar extra capabilities beyond the basics, and even RSVP (which is pretty lightweight) hasRSVP.hash
for shallowly settling any promise properties of an object. And the Q ecosystem is built around different fundamental primitives from DOM Futures which allow things like promise for remote objects, promise pipelining, and the like---use cases which are increasingly important.To think that users who are accustomed to this level of flexibility are going to suddenly switch to DOM Futures/ECMAScript promises is very naive. More likely, those will be used alongside more full-featured promises returned from other parts of the system---forever, not just in some transition period. Thus, interop is going to be necessary for an ergonomic experience.
I never suggested to give all that up. In the long term, all of that can be re-implemented on top of platform promises (with devtools support which is a significant bonus; you were talking about stack traces?) and maybe even improved based on the experience of current libraries. For the transition period, I suggested:
Each library can add: if(nativeFuture(p)) p = wrapNativeFuture(p)
to the beginning each of its method accepting a promise as argument (or equivalent is the promise is in 'this')
These 2 lines (+wrapNativeFuture) sound pretty practical to me for the transition period.
It sounds like a reasonable compromise for libraries to work with built-in promises without imposing a burden on top of these.
Dean Landolt wrote:
FWIW I disagree with him -- I strongly suspect that by the time this were to all go down and a stable polyfill existed there'd already be too much then-demanding code in the wild. There probably already is. And at that point it's proto all over again -- the standard will have no choice but to respect then and the problem cannot be fixed :-/
There is a major difference with proto which is that it is a platform de facto standard not a library de facto standard. proto is already in the platform, it makes sense to standardize it as part of the platform. ".then" is a convention among a particular dev community (which I consider to be part of, but that's beyond the point); I'm not sure why the platform should follow that convention.
By that logic, should the intersection of jQuery and Zepto's $ be included in the platform? Or the intersection of Underscore and Lodash for _ (there are minor differences)?
On Mon, Apr 22, 2013 at 1:15 PM, David Bruant <bruant.d at gmail.com> wrote:
Le 22/04/2013 17:16, Domenic Denicola a écrit :
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native
(or polyfilled) have to cohabit with previous library promises?
This is a really bad misconception that you have repeated several times now.
DOM Futures, and possibly ECMAScript promises, are practically feature-less. They have
then
,catch
sugar, a few static combinator methods, and that's it. If you want to do anything serious with promises, you're going to need a lot more than that. Check out the extensive API Q provides, for example:kriskowal/**q/wiki/API-Referencekriskowal/q/wiki/API-Reference
In particular, I and teams I have worked on in real-world projects use
promise.finally
, the promise-for-object methods, the promise-for-function methods, and the utility methods literally every day. (To say nothing of the Node.js-interfacing methods.) Features like long stack traces have been invaluable for us. When.js has similar extra capabilities beyond the basics, and even RSVP (which is pretty lightweight) hasRSVP.hash
for shallowly settling any promise properties of an object. And the Q ecosystem is built around different fundamental primitives from DOM Futures which allow things like promise for remote objects, promise pipelining, and the like---use cases which are increasingly important.To think that users who are accustomed to this level of flexibility are going to suddenly switch to DOM Futures/ECMAScript promises is very naive. More likely, those will be used alongside more full-featured promises returned from other parts of the system---forever, not just in some transition period. Thus, interop is going to be necessary for an ergonomic experience.
I never suggested to give all that up. In the long term, all of that can be re-implemented on top of platform promises (with devtools support which is a significant bonus; you were talking about stack traces?) and maybe even improved based on the experience of current libraries. For the transition period, I suggested:
Each library can add:
if(nativeFuture(p)) p = wrapNativeFuture(p)
to the beginning each of its method accepting a promise as argument (or equivalent is the promise is in 'this')
These 2 lines (+wrapNativeFuture) sound pretty practical to me for the transition period.
It sounds like a reasonable compromise for libraries to work with built-in promises without imposing a burden on top of these.
Dean Landolt wrote:
FWIW I disagree with him -- I strongly suspect that by the time this were to all go down and a stable polyfill existed there'd already be too much then-demanding code in the wild. There probably already is. And at that point it's proto all over again -- the standard will have no choice but to respect then and the problem cannot be fixed :-/
There is a major difference with proto which is that it is a platform de facto standard not a library de facto standard. proto is already in the platform, it makes sense to standardize it as part of the platform. ".then" is a convention among a particular dev community (which I consider to be part of, but that's beyond the point); I'm not sure why the platform should follow that convention.
It was an imperfect analogy -- I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong!
By that logic, should the intersection of jQuery and Zepto's $ be included in the platform? Or the intersection of Underscore and Lodash for _ (there are minor differences)?
Apples and oranges -- these two are globals and have nothing to do with a ducktyped defacto method.
On Mon, Apr 22, 2013 at 12:47 PM, Kevin Smith <zenparsing at gmail.com> wrote:
FWIW I disagree with him -- I strongly suspect that by the time this
were to all go down and a stable polyfill existed there'd already be too much then-demanding code in the wild. There probably already is. And at that point it's proto all over again -- the standard will have no choice but to respect then and the problem cannot be fixed :-/
Why not? If the
then
symbol is well-known (e.g. easily imported from somewhere), then why can't libraries be upgraded to use it as an alias for theirthen
method?
I would love to see this, but best I can tell it can't be a straitforward polyfill. The necessary infrastructure has to settle, and what are Promises/A+ implementers supposed to do in the meantime?
Unless a reasonably elegant solution can be found I suspect any es promises proposal will include support for thenables. I spent a lot of time thinking about it a few years back and couldn't find one, so I remain skeptical.
I would love to see this, but best I can tell it can't be a straitforward polyfill. The necessary infrastructure has to settle, and what are Promises/A+ implementers supposed to do in the meantime?
Presumably the standard version of Future would provide some convenient way to get the symbol. The library would use that to conditionally provide the symbol-named alias. Something along these lines:
if (typeof Future !== "undefined")
MyPromise.prototype[Future.thenSymbol] = MyPromise.prototype.then;
No?
On Mon, Apr 22, 2013 at 1:53 PM, Kevin Smith <zenparsing at gmail.com> wrote:
I would love to see this, but best I can tell it can't be a
straitforward polyfill. The necessary infrastructure has to settle, and what are Promises/A+ implementers supposed to do in the meantime?
Presumably the standard version of Future would provide some convenient way to get the symbol. The library would use that to conditionally provide the symbol-named alias. Something along these lines:
if (typeof Future !== "undefined") MyPromise.prototype[Future.thenSymbol] = MyPromise.prototype.then;
No?
Perhaps, depending on how all the current libs are being used in the wild. But this assumption does breaks the ducktyping as defined by the spec. Who knows -- this may not be a problem in practice.
Alternatively it occurs to me that now that we'll have mutable proto to lean on we could specify something like Promises/A++ as Promises/A+ with the additional constraint that some Promise (or Future, or whatever) builtin exist on the proto chain of any promise. This would allow for instanceof checks to be interoperable across libraries. And really, you don't even really need mutable proto -- this is only necessary for pure-userland interoperability. Since this a standard builtin the problems with Promises/C go away -- even when polyfilled all promise libs could expect to see the same base class. That is, IIRC -- it's been a long time since I've really chewed this over.
So I guess I'm less skeptical. Promise (and an accompanying Promise.when) could be defined and shimmed into existing tool chains today. A huge chunk of currently working Promises/A+ code would break without a corresponding Promise.prototype.then, but nothing would stop lib authors from adding it. And even without it the migration path would be straightforward and could even be automated.
Okay, I'm convinced. But I don't matter -- both TC39 and the Promises/A+ implementers have to be.
Le 22/04/2013 19:32, Dean Landolt a écrit :
I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong!
I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.
CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing.
David
[1] dom.spec.whatwg.org/#concept-resolver-resolve [2] casperjs.org [3] casperjs.org/api.html#casper.then [4] laurentj/slimerjs
On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit :
I was just saying there will be pressure to include support for thenables
(already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong!
I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.
CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
What do you mean by the promise subclass trick? If you mean that Casper
instances would subclass Promise I don't think that'd work out too well as
is. Promises/A (and I presume A+) intentionally specified then
to return
a new promise, so Casper would have to change in a very breaking way to
pull this off.
In any case, considering that an object with a 'then' function is a promise
is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing
by the day) may be too much to bear. Hopefully not -- it's really very
simple for Promises/A+ libs to add then
to the Promise prototype.
I don't think in the entire platform there is a precedent of doing this
(maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P
Le 23/04/2013 14:56, Dean Landolt a écrit :
On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit : I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong! I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things. CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
What do you mean by the promise subclass trick? If you mean that Casper instances would subclass Promise I don't think that'd work out too well as is. Promises/A (and I presume A+) intentionally specified
then
to return a new promise, so Casper would have to change in a very breaking way to pull this off.
I'm not sure it would be a breaking change for the vast majority of people. They may actually like that instead of being forced to to casper.start. They may also enjoy being able to chain .then-s. But maybe it's breaking.
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear.
What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add
then
to the Promise prototype.
Aren't they already doing that? I don't understand your point here.
I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P
Once again, proto is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
On Tue, Apr 23, 2013 at 9:12 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 23/04/2013 14:56, Dean Landolt a écrit :
On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit :
I was just saying there will be pressure to include support for
thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong!
I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.
CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
What do you mean by the promise subclass trick? If you mean that Casper instances would subclass Promise I don't think that'd work out too well as is. Promises/A (and I presume A+) intentionally specified
then
to return a new promise, so Casper would have to change in a very breaking way to pull this off.I'm not sure it would be a breaking change for the vast majority of people. They may actually like that instead of being forced to to casper.start. They may also enjoy being able to chain .then-s. But maybe it's breaking.
IIRC you can already chain thens -- the break is that you will be forced to chain thens in order to maintain your intended chronology. Inheriting from Promise would suggest all those then calls will be fired in parallel instead of happening in serial.
Although the only time I tried Casper (~ a year ago) I gave up on it because I couldn't get this stuff right anyway.
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Apologies, I wasn't very clear. I completely agree with this point and was just trying to reinforce it :)
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear.
What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add
then
to the Promise prototype.Aren't they already doing that? I don't understand your point here.
No, DOMFutures ships with this OOTB. If there were an es Promise or Future
builtin I suspect there would be a lot of pressure to specify it with
then
on the prototoype to make its instances compatible with existing
Promises/A+ libs. That's the crushing weight of legacy I'm referring to.
What occurred to me is that it really is just a few lines of code for each
of these Promises/A+ libs to add to tack on the prototype then
without
having to muddy the spec. In hindsight this seems obvious. I wonder why
DOMFutures didn't go this route? It may not be too late.
I don't think in the entire platform there is a precedent of doing this
(maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P
Once again, proto is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think proto is a great example. And again, I think it helps make your (* our*) point :)
Le 23/04/2013 15:27, Dean Landolt a écrit :
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand. I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear.
What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add `then` to the Promise prototype.
Aren't they already doing that? I don't understand your point here.
No, DOMFutures ships with this OOTB. If there were an es Promise or Future builtin I suspect there would be a lot of pressure to specify it with
then
on the prototoype to make its instances compatible with existing Promises/A+ libs. That's the crushing weight of legacy I'm referring to.What occurred to me is that it really is just a few lines of code for each of these Promises/A+ libs to add to tack on the prototype
then
without having to muddy the spec. In hindsight this seems obvious. I wonder why DOMFutures didn't go this route? It may not be too late.
Until it is widely deployed, everything is possible. What browsers will choose/accept to implement will be the standard. And indeed, I believe existing libs could adapt easily to platform Futures even if these don't follow Promise/A+.
I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing. IMHO __proto__ is one precedent -- and we know how that's going :P
Once again, __proto__ is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think proto is a great example. And again, I think it helps make your (our) point :)
Oh I see. Then yes, in that sense proto is a relevant comparison (sorry for the misunderstanding). Though, the confusion for proto is less likely because first, most people were aware of it (which really isn't clear for promises/A+) and __ is the sign for both "be careful" and "it's unlikely to collide with something else" which isn't the case for "then".
On Tue, Apr 23, 2013 at 9:37 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 23/04/2013 15:27, Dean Landolt a écrit :
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear.
What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add
then
to the Promise prototype.Aren't they already doing that? I don't understand your point here.
No, DOMFutures ships with this OOTB. If there were an es Promise or Future builtin I suspect there would be a lot of pressure to specify it with
then
on the prototoype to make its instances compatible with existing Promises/A+ libs. That's the crushing weight of legacy I'm referring to.What occurred to me is that it really is just a few lines of code for each of these Promises/A+ libs to add to tack on the prototype
then
without having to muddy the spec. In hindsight this seems obvious. I wonder why DOMFutures didn't go this route? It may not be too late.Until it is widely deployed, everything is possible. What browsers will choose/accept to implement will be the standard. And indeed, I believe existing libs could adapt easily to platform Futures even if these don't follow Promise/A+.
True.
I don't think in the entire platform there is a precedent of doing this
(maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P
Once again, proto is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think proto is a great example. And again, I think it helps make your ( our) point :)
Oh I see. Then yes, in that sense proto is a relevant comparison (sorry for the misunderstanding). Though, the confusion for proto is less likely because first, most people were aware of it (which really isn't clear for promises/A+) and __ is the sign for both "be careful" and "it's unlikely to collide with something else" which isn't the case for "then".
Agreed. And now that it looks like only the unquoted version of proto will be special the analogy pretty much falls apart in es6. Which is awesome.
The ‘await’ keyword in C# is paired with a special function declaration (similar to function* as proposed for ES6) that can be used in one of three ways:
async void Foo() { ... } async Task Foo() { ... } async Task<T> Foo() { ... }
The ‘async’ keyword here flags the method for special case handing by the compiler to turn the function body into a state machine. The kind of state machine generated is based on the return type of the method signature.
The “async void” method creates a state machine that inherently throws any unhandled exception to the UnhandledException trap/event of the AppDomain. This is often useful for a “fire-and-forget” operation where the caller is not concerned with the result. An “async void" method cannot be awaited by the caller as it has no return type.
The “async Task” method signature creates a state machine that returns a Task (nee Future) that will eventually signal the successful completion or the exception thrown by the function. It is roughly analogous to a “void” method signature as it has no result, and the Task is only used for further “await” expressions or defining continuations using .ContinueWith (analog to Future#then). As the method returns a Task, any exception from the method will not be thrown to the AppDomain and therefore should be “await”ed appropriately (alternatively, the Task.Wait() method can be called that will block the current thread until the task completes and will throw any unhandled exceptions).
The “async Task<T>” method signature creates a state machine that returns a Task<T> that will provide either the eventual return value (of type T) or throw the exception. As with “async Task”, the exception must be explicitly handled by the caller either through an “await”, ContinueWith, or Wait.
Providing a special syntax for an ES function (either function* or function^) only goes half way to solving the issues of supporting “await”, as it would require some additional special effort to handle Future swallowing or bubbling the exception.
I have been experimenting with an (unofficial & unsupported) fork of TypeScript that supports ES6 generators by transpiling into a state machine in the function body. Albeit slower than a native implementation, it allows for some experimentation with the syntax in any ES5 compatible browser. TypeScript has the advantage of static type checking during compile time, which allows me to mimic the C# behavior for the “async” keyword. As such I also have been experimenting with adding async/await to TypeScript and early indicators show that its is a very convenient way of handling asynchronous development flows. An example of a async method in this implementation looks something like:
class AuthAPI { async getAuthProvidersAsync(): Promise { var providers = await HttpRequestAsync.get(acsUrl, settings); return JSON.parse(providers); } }
class AuthView { providers = ko.observableArray(); async load(): void { var authAPI = new AuthAPI(); var providers = <IAuthProvider[]>await authAPI.getAuthProvidersAsync(); this.providers(providers); } }
As opposed to the non-await implementation (with Futures/Promises):
class AuthAPI { getAuthProvidersAsync(): Promise { return HttpRequestAsync.get(acsUrl, settings) .then(p => JSON.parse(p)); } }
class AuthView { providers = ko.observableArray(); load(): void { var authAPI = new AuthAPI(); authAPI.done((providers: IAuthProvider[]) => { this.providers(providers); }); } }
While this example may not show a lot of power, imagine an async polling mechanism:
class AppAPI { async waitFor(eventId: string): Promise { while (true) { var result = await HttpRequestAsync.get(eventUri + “?eventId=” + eventId); var obj = JSON.parse(result); if (obj.complete) { return obj.url; } await Promise.sleep(500); } } }
Writing that using then is much more cumbersome:
class AppAPI { waitFor(eventId: string): Promise { return new Promise(resolver => { var awaiter = () => { HttpRequestAsync.get(eventUri + “?eventId=” + eventId).done(result => { try { var obj = JSON.parse(result); if (obj.complete) { resolver.resolve(obj.url); return; } setTimeout(awaiter, 500); } catch (e) { resolver.reject(e); } }, resolver.reject); awaiter(); } }); } }
Adding “await” would be a valuable addition to the language, assuming we can solve the “swallowed exception” issues, or at least call out that it is a caveat to developers that should be handled explicitly.
Another way to get async/await-like capabilities might be to combine the Q.async/task.spawn and yield functionality with something like Python-style decorators. I’ve been working on a proposal for decorators for both TypeScript and ES (and have another unofficial/experimental fork of TypeScript with support for this up on CodePlex today). If that were the case, you could fake async/await without having to directly wrap the function call as is necessary for Q.async/task.spawn today:
class Foo { // example decorator using Python-style syntax (@ token may collide with symbols/private) @async myAsync() { var a = yield myOtherAsync(); } }
Ron
Sent from Windows Mail
From: Dean Landolt Sent: Tuesday, April 23, 2013 6:28 AM To: David Bruant Cc: Brendan Eich, Mark S. Miller, Douglas Crockford, public-script-coord at w3.org, Norbert Lindenberg, Markus Lanthaler, EcmaScript
On Tue, Apr 23, 2013 at 9:12 AM, David Bruant <bruant.d at gmail.com<mailto:bruant.d at gmail.com>> wrote:
Le 23/04/2013 14:56, Dean Landolt a écrit : On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d at gmail.com<mailto:bruant.d at gmail.com>> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit :
I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong! I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.
CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
What do you mean by the promise subclass trick? If you mean that Casper instances would subclass Promise I don't think that'd work out too well as is. Promises/A (and I presume A+) intentionally specified then
to return a new promise, so Casper would have to change in a very breaking way to pull this off.
I'm not sure it would be a breaking change for the vast majority of people. They may actually like that instead of being forced to to casper.start. They may also enjoy being able to chain .then-s. But maybe it's breaking.
IIRC you can already chain thens -- the break is that you will be forced to chain thens in order to maintain your intended chronology. Inheriting from Promise would suggest all those then calls will be fired in parallel instead of happening in serial.
Although the only time I tried Casper (~ a year ago) I gave up on it because I couldn't get this stuff right anyway.
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Apologies, I wasn't very clear. I completely agree with this point and was just trying to reinforce it :)
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear. What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add then
to the Promise prototype.
Aren't they already doing that? I don't understand your point here.
No, DOMFutures ships with this OOTB. If there were an es Promise or Future builtin I suspect there would be a lot of pressure to specify it with then
on the prototoype to make its instances compatible with existing Promises/A+ libs. That's the crushing weight of legacy I'm referring to.
What occurred to me is that it really is just a few lines of code for each of these Promises/A+ libs to add to tack on the prototype then
without having to muddy the spec. In hindsight this seems obvious. I wonder why DOMFutures didn't go this route? It may not be too late.
I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P Once again, proto is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think proto is a great example. And again, I think it helps make your (our) point :)
On Tue, Apr 23, 2013 at 6:12 AM, David Bruant <bruant.d at gmail.com> wrote:
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Agreed. I'll note, though, that if the semantics of .resolve() are non-recursive (it strips off one layer of promise/future, but no further), then this becomes somewhat less of an issue, as it can be worked around - if you want to return something that's not a promise, just wrap it in a fulfilled promise (via some sugar function - I've proposed such in www-dom). That wrapper will be unwrapped straight away, and the future accepted with the value within.
The big problem only arrives if you both treat all thenables as promises/futures, and have recursive resolve semantics, because then you can't ever return a non-promise with a .then() method.
I'd prefer that resolve only strip away a branded future, not an arbitrary thenable, and for there to be a way to brand a thenable as a future. That way, using other-library promises just requires one trip through the branding function and they can interoperate. Existing libraries tend to expose the same mechanism already, so that the other-library promises expose all the proper methods of the "main" library.
But I want non-recursive resolve more, and if I was forced to choose one, would take monadic resolve over branded futures.
I fall mostly under the "native futures should not implicitly chain library futures" camp. Its easy enough to write:
var p = // some thenable return new Future(resolver => p.then(resolver.resolve, resolver.reject);
One thing that I find a bit icky is that implicitly attaching to "then" not only is not a guarantee (is it really a "thennable"), but it also leaks a new thenable that may take up more memory and processing time due to possible scheduling of results/errors to the chained (and unused) thenable. It would be slightly better to hook "done" rather than "then". Depending on the various implementations, duck type checking for both "then" and "done" might be slightly more reliable than just looking for "then" and would let us hook "done" to reduce overhead.
If library authors subclass Future, the @class symbol could be easily checked in Future#resolve to chain appropriately, though the Future superclass might need to be able to construct a new instance of the subclass for calls to Future#then, otherwise you loose the capabilities of the subclass after the first call to Future#then.
Library authors that do not subclass Future could add something like .asFuture() to their prototype (or use a predefined symbol), just as one might to support iterators.
Alternatively, ES might also need to expose a FutureFactory that can be used to create instances of a Future subclass. If that were the case, the Future constructor could take in an optional FutureFactory subclass that is used to construct library subclasses of Future.
Ron
Sent from my Windows Phone
On Tue, Apr 23, 2013 at 11:25 AM, David Sheets <kosmo.zb at gmail.com> wrote:
On Tue, Apr 23, 2013 at 7:02 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Tue, Apr 23, 2013 at 6:12 AM, David Bruant <bruant.d at gmail.com> wrote:
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Agreed. I'll note, though, that if the semantics of .resolve() are non-recursive (it strips off one layer of promise/future, but no further), then this becomes somewhat less of an issue, as it can be worked around - if you want to return something that's not a promise, just wrap it in a fulfilled promise (via some sugar function - I've proposed such in www-dom). That wrapper will be unwrapped straight away, and the future accepted with the value within.
lists.w3.org/Archives/Public/www-dom/2013AprJun/0051.html? Future.of == "monadic return"? Doesn't seem too sugary...
It's sugar because you already achieve it with existing functionality. Instead of "var f = Future.of(v);", you can write:
var f = new Future(function(r) { r.accept(v); });
or, in ES6:
var f = new Future(r=>r.accept(v));
However, I think it's sufficiently useful sugar that it bears adding.
And yes, it's the monadic return.
The big problem only arrives if you both treat all thenables as promises/futures, and have recursive resolve semantics, because then you can't ever return a non-promise with a .then() method.
I'd prefer that resolve only strip away a branded future, not an arbitrary thenable, and for there to be a way to brand a thenable as a future. That way, using other-library promises just requires one trip through the branding function and they can interoperate. Existing libraries tend to expose the same mechanism already, so that the other-library promises expose all the proper methods of the "main" library.
But I want non-recursive resolve more, and if I was forced to choose one, would take monadic resolve over branded futures.
"non-recursive resolve" == "monadic run" == "comonadic extract"? "recursive resolve" == "monadic run" compose { fixpoint of "monadic join" }?
Yes. (Comonadic "extend", not "extract".)
Will Futures be associative and have left and right identity between bind ("then") and return ("of")? Will this be specified explicitly?
Yes, they do. It's part of the basic mechanics of the two functions.
If libraries are able to create Future-like objects that interoperate with built-in Futures, will they be required (ha) to satisfy these equivalences (associativity, identity)?
"Required" in the same sense that Haskell "requires" that things which implement the Monad typeclass satisfy the monad laws - nothing enforces the restrictions, but things might be wonky if you don't.
+everybody ☺
From: Kevin Gadd [mailto:kevin.gadd at gmail.com] Sent: Tuesday, April 23, 2013 5:32 PM To: Ron Buckton Subject: Re: Futures
Whoops, I replied privately instead of on-list. You might want to forward your stuff to the list.
On Tue, Apr 23, 2013 at 5:28 PM, Ron Buckton <rbuckton at chronicles.org<mailto:rbuckton at chronicles.org>> wrote:
While a bit confusing, it has the upside of being explicit about what happens to your exceptions. That still doesn’t prevent developers from forgetting to “await” an “async Task” function and lose the exception.
If “await” is part of the language, I’d almost want any ignored return value that is a built-in Future to implicitly have its Future#done method called to ensure the exception is bubbled up as an unhandled exception. I can see how that can cause issues with developer expectations and would probably be too much of an over-optimization. At the very least, any kind of linter or static verification could look for a call to a function^ (if it can be resolved) and write warnings if you don’t either “await” it or call .then or .done.
A variant of “async void” might be feasible with an aforementioned Python decorator capability:
function done(fn) {
return function() {
var result = fn.apply(this, arguments);
if (Future.isFuture(result)) {
result.done();
}
} }
@done function^ myAsync() {
await someOtherAsync();
}
myAsync(); // fire and forget, any error is bubbled up to the global unhandled exception logic
Ron
From: Kevin Gadd [mailto:kevin.gadd at gmail.com<mailto:kevin.gadd at gmail.com>]
Sent: Tuesday, April 23, 2013 5:11 PM To: Ron Buckton Subject: Re: Futures
Thanks. Didn't know async void worked that way (gross). -kg (mobile) Ron Buckton <rbuckton at chronicles.org<mailto:rbuckton at chronicles.org>> wrote:
The ‘await’ keyword in C# is paired with a special function declaration (similar to function* as proposed for ES6) that can be used in one of three ways:
async void Foo() { ... } async Task Foo() { ... } async Task<T> Foo() { ... }
The ‘async’ keyword here flags the method for special case handing by the compiler to turn the function body into a state machine. The kind of state machine generated is based on the return type of the method signature.
The “async void” method creates a state machine that inherently throws any unhandled exception to the UnhandledException trap/event of the AppDomain. This is often useful for a “fire-and-forget” operation where the caller is not concerned with the result. An “async void" method cannot be awaited by the caller as it has no return type.
The “async Task” method signature creates a state machine that returns a Task (nee Future) that will eventually signal the successful completion or the exception thrown by the function. It is roughly analogous to a “void” method signature as it has no result, and the Task is only used for further “await” expressions or defining continuations using .ContinueWith (analog to Future#then). As the method returns a Task, any exception from the method will not be thrown to the AppDomain and therefore should be “await”ed appropriately (alternatively, the Task.Wait() method can be called that will block the current thread until the task completes and will throw any unhandled exceptions).
The “async Task<T>” method signature creates a state machine that returns a Task<T> that will provide either the eventual return value (of type T) or throw the exception. As with “async Task”, the exception must be explicitly handled by the caller either through an “await”, ContinueWith, or Wait.
Providing a special syntax for an ES function (either function* or function^) only goes half way to solving the issues of supporting “await”, as it would require some additional special effort to handle Future swallowing or bubbling the exception.
I have been experimenting with an (unofficial & unsupported) fork of TypeScript that supports ES6 generators by transpiling into a state machine in the function body. Albeit slower than a native implementation, it allows for some experimentation with the syntax in any ES5 compatible browser. TypeScript has the advantage of static type checking during compile time, which allows me to mimic the C# behavior for the “async” keyword. As such I also have been experimenting with adding async/await to TypeScript and early indicators show that its is a very convenient way of handling asynchronous development flows. An example of a async method in this implementation looks something like:
class AuthAPI { async getAuthProvidersAsync(): Promise { var providers = await HttpRequestAsync.get(acsUrl, settings); return JSON.parse(providers); } }
class AuthView { providers = ko.observableArray(); async load(): void { var authAPI = new AuthAPI(); var providers = <IAuthProvider[]>await authAPI.getAuthProvidersAsync(); this.providers(providers); } }
As opposed to the non-await implementation (with Futures/Promises):
class AuthAPI { getAuthProvidersAsync(): Promise { return HttpRequestAsync.get(acsUrl, settings) .then(p => JSON.parse(p)); } }
class AuthView { providers = ko.observableArray(); load(): void { var authAPI = new AuthAPI(); authAPI.done((providers: IAuthProvider[]) => { this.providers(providers); }); } }
While this example may not show a lot of power, imagine an async polling mechanism:
class AppAPI { async waitFor(eventId: string): Promise { while (true) { var result = await HttpRequestAsync.get(eventUri + “?eventId=” + eventId); var obj = JSON.parse(result); if (obj.complete) { return obj.url; } await Promise.sleep(500); } } }
Writing that using then is much more cumbersome:
class AppAPI { waitFor(eventId: string): Promise { return new Promise(resolver => { var awaiter = () => { HttpRequestAsync.get(eventUri + “?eventId=” + eventId).done(result => { try { var obj = JSON.parse(result); if (obj.complete) { resolver.resolve(obj.url); return; } setTimeout(awaiter, 500); } catch (e) { resolver.reject(e); } }, resolver.reject); awaiter(); } }); } }
Adding “await” would be a valuable addition to the language, assuming we can solve the “swallowed exception” issues, or at least call out that it is a caveat to developers that should be handled explicitly.
Another way to get async/await-like capabilities might be to combine the Q.async/task.spawn and yield functionality with something like Python-style decorators. I’ve been working on a proposal for decorators for both TypeScript and ES (and have another unofficial/experimental fork of TypeScript with support for this up on CodePlex today). If that were the case, you could fake async/await without having to directly wrap the function call as is necessary for Q.async/task.spawn today:
class Foo { // example decorator using Python-style syntax (@ token may collide with symbols/private) @async myAsync() { var a = yield myOtherAsync(); } }
Ron
Sent from Windows Mail
From: Dean Landolt Sent: Tuesday, April 23, 2013 6:28 AM To: David Bruant Cc: Brendan Eich, Mark S. Miller, Douglas Crockford, public-script-coord at w3.org<mailto:public-script-coord at w3.org>, Norbert Lindenberg, Markus Lanthaler, EcmaScript
On Tue, Apr 23, 2013 at 9:12 AM, David Bruant <bruant.d at gmail.com<mailto:bruant.d at gmail.com>> wrote:
Le 23/04/2013 14:56, Dean Landolt a écrit : On Tue, Apr 23, 2013 at 4:54 AM, David Bruant <bruant.d at gmail.com<mailto:bruant.d at gmail.com>> wrote:
Le 22/04/2013 19:32, Dean Landolt a écrit :
I was just saying there will be pressure to include support for thenables (already in DOMFutures). If you believe otherwise don't let me dissuade you -- I would absolutely love it if I were proven wrong! I guess it would take making sure no content can be confused by the current steps 3-5 of the current resolve spec [1]. I believe browser vendors have tools to study this kind of things.
CasperJS [2] create objects with a then method [3]. Interestingly, this doesn't run in the browser (until someone decides to re-implement it of top of a web browser or FirefoxOS. [4] ?). Potentially more interestingly, Casper objects could be promises subclasses (to be considered). It wouldn't be surprising if there were content on the web where the promise subclass trick couldn't work.
What do you mean by the promise subclass trick? If you mean that Casper instances would subclass Promise I don't think that'd work out too well as is. Promises/A (and I presume A+) intentionally specified then
to return a new promise, so Casper would have to change in a very breaking way to pull this off.
I'm not sure it would be a breaking change for the vast majority of people. They may actually like that instead of being forced to to casper.start. They may also enjoy being able to chain .then-s. But maybe it's breaking.
IIRC you can already chain thens -- the break is that you will be forced to chain thens in order to maintain your intended chronology. Inheriting from Promise would suggest all those then calls will be fired in parallel instead of happening in serial.
Although the only time I tried Casper (~ a year ago) I gave up on it because I couldn't get this stuff right anyway.
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Apologies, I wasn't very clear. I completely agree with this point and was just trying to reinforce it :)
In any case, considering that an object with a 'then' function is a promise is a recipe for confusion. Promise/A+ folks asked for and are happy about it. The rest of the webdevs who aren't aware of this subtle non-intuitive rule will have a very hard time when, for whatever reason, they have a 'then' function in a resolve value and their code really behaves in a way they don't understand.
I agree it's a recipe for confusion. But the weight of legacy code (growing by the day) may be too much to bear. What about the weight of legacy non-promise code using "then"? The best thing we can say now is that we know nothing about it and it'd be wise to wait until more data on "then" is available. The Casper example should at least worry us. I hope it will be the browser vendors choice.
Hopefully not -- it's really very simple for Promises/A+ libs to add then
to the Promise prototype.
Aren't they already doing that? I don't understand your point here.
No, DOMFutures ships with this OOTB. If there were an es Promise or Future builtin I suspect there would be a lot of pressure to specify it with then
on the prototoype to make its instances compatible with existing Promises/A+ libs. That's the crushing weight of legacy I'm referring to.
What occurred to me is that it really is just a few lines of code for each of these Promises/A+ libs to add to tack on the prototype then
without having to muddy the spec. In hindsight this seems obvious. I wonder why DOMFutures didn't go this route? It may not be too late.
I don't think in the entire platform there is a precedent of doing this (maybe for a good reason?). We'll see what web browsers end up implementing.
IMHO proto is one precedent -- and we know how that's going :P Once again, proto is not a good comparison. It's already in the platform. As far as promises are concerned, the platform has exactly no obligation to follow the current Future or an alternative that'll emerge tomorrow; no obligation to follow Promise/A+ or whatever else.
You said "I don't think in the entire platform there is a precedent of doing this". I assume by "this" you meant using a forgeable string key that could lead to confusion. That is what we were discussing, right? If so I think proto is a great example. And again, I think it helps make your (our) point :)
Tab Atkins Jr. wrote:
On Sat, Apr 20, 2013 at 6:17 AM, Brendan Eich<brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value, Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation.
Yeah, that's fine when you're explicitly invoking something that needs to return two objects.
You didn't grok what I wrote. The object is erased, and the value(s) are returned to the caller depending on the pattern it uses.
It's less fine when you have something like a hypothetical getJSON() (future-returning XHR sugar), which can reasonably just return a future, but which we also want to make cancellable. Burdening every call site
No, you can use a smaller pattern, e.g.
let { future } = getJSON();
instead of
let { future, cancel } = getJSON();
with the need to pull out the future from the returned object
No, see above.
isn't great.
HTH.
Brendan Eich wrote:
The object is erased
Meaning, not allocated.
Perhaps Future should have a static Future.cancelable method (similar to Proxy.revocable):
let { future, cancel } = Future.cancelable(function(resolver) { .. do future stuff .. });
You would still need a means to hook cancellation, possibly by replacing cancel to the caller:
function getJSON() { var xhr = new XMLHttpRequest(); ... let { future, cancel } = Future.cancelable(...); return { future, cancel: function() { cancel(); xhr.abort(); } }; }
Ron
Ron Buckton wrote:
Perhaps Future should have a static Future.cancelable method (similar to Proxy.revocable):
Matching the two APIs seems like a win, all else equal.
let { future, cancel } = Future.cancelable(function(resolver) { .. do future stuff .. });
You would still need a means to hook cancellation, possibly by replacing cancel to the caller:
function getJSON() { var xhr = new XMLHttpRequest(); ... let { future, cancel } = Future.cancelable(...); return { future, cancel: function() { cancel(); xhr.abort(); } }; }
The other advantage of the Proxy.revocable pattern, as Tom pointed out, is its OCap-ness; you can't use the returned revoke on some other revocable proxy. Same argument would apply to Future.cancelable.
On Tue, Apr 23, 2013 at 6:41 PM, Brendan Eich <brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
Yeah, that's fine when you're explicitly invoking something that needs to return two objects.
You didn't grok what I wrote. The object is erased, and the value(s) are returned to the caller depending on the pattern it uses.
It's less fine when you have something like a hypothetical getJSON() (future-returning XHR sugar), which can reasonably just return a future, but which we also want to make cancellable. Burdening every call site
No, you can use a smaller pattern, e.g.
let { future } = getJSON();
instead of
let { future, cancel } = getJSON();
No, I absolutely grok what you wrote. I still think that burdening the call-site with the need to pull a future out of the object like that (even though, yes, the object itself will be optimized out by future VMs) is too much. It also means that we can't upgrade a function from returning a future to returning a cancellable future - we'll have to mint a new name.
I think my preferred method is to have cancellable futures be a subtype of futures, which expose a .resolver property on themselves, and which return a normal future from their .then() function. (It doesn't make sense for the chained future to continue to be cancellable, because what's being cancelled? It's not natural, I think, to let a downstream future hold a resolver for an upstream one. Thus, there's no need for the downstream to be cancellable at all.)
This keeps the two objects separate, and lets you "clean" a cancellable future for passing to untrusted code by calling .then() with no arguments.
Tab Atkins Jr. wrote:
I still think that burdening the call-site with the need to pull a future out of the object like that (even though, yes, the object itself will be optimized out by future VMs) is too much.
This started (for me) when you wrote:
It would be so nice if JS had multiple return values, so we could let
cancellable future-returning APIs just return a naked resolver as their second value,
But now that I've pointed out JS has multiple return values, you're not happy :-P.
Don't worry about VMs, it's not important to fret about their poor lot in life. Premature optimization is root of evil and all that. If this case (or the Proxy.revocable one) became important enough for functions to look into their continuation and optimize away their returned object, VM'll do that.
Having to name the properties is good, it makes the API self-describing.
Sorry for the late post. Just wanted to agree with you assessment of the landscape and options. We should not let theoretical purity poison the utility of this feature.
On Apr 22, 2013 5:29 PM, "Mark S. Miller" <erights at google.com> wrote:
On Mon, Apr 22, 2013 at 8:16 AM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
From: David Bruant [bruant.d at gmail.com]
Especially given that it's only for a transitioning period where native (or polyfilled) have to cohabit with previous library promises?
This is a really bad misconception that you have repeated several times now.
Hi Domenic, I just repeated it too:
If [standard platform-provided ES promises] ever wins definitively, so that there are no remaining libraries of concern, it will no longer need the assimilation mechanisms that enabled that co-existence; but by then it will be too late to shed it.
I agree that Promises/A+ are too minimal, that DOMFutures does not solve the problems that need solving,
This is far too strong a statement. They do not solve all problems, but the crack the hardest not: they add futures/promises to the design language of the entire platform. We forget that value at our loss. The proposed, minimal, initial Futures design is a starting point based on design consensus. As that consensus expands, so can (hopefully will!) the API.
I'm happy to work with all who are interested on such extensions.
Le 24/04/2013 11:27, Alex Russell a écrit :
Sorry for the late post. Just wanted to agree with you assessment of the landscape and options. We should not let theoretical purity poison the utility of this feature.
Futures as currently spec'ed misbehave in the presence of objects with a "then" method which are not promises. Web devs will hit this and the bugs they'll file against conformant web browsers believing they misbehave won't be purely theorical.
Le 20/04/2013 16:03, Brendan Eich a écrit :
David Bruant wrote:
Le 20/04/2013 15:17, Brendan Eich a écrit :
Tab Atkins Jr. wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
Hello, destructuring:
let{ proxy, revoke} = Proxy.revocable(target, handler);
from strawman:revokable_proxies. Or use an array pattern if you prefer.
JIT'ing VMs can optimize these pretty easily to avoid object allocation. I find pretty interesting cases where high-level expressive syntax results in optimizations that were impossible to very hard without the syntax. Is it already implemented in SpiderMonkey? If not, is there a bug number to follow the progress?
Implemented long ago:
js> function cons(h, t) { return {head: h, tail: t}; } js> let {head, tail} = cons("hi", "bye") js> head "hi" js> tail "bye"
(Not optimized yet, AFAIK. Care to file that bug? Cc: me if you do. Thanks!)
Filed bugzilla.mozilla.org/show_bug.cgi?id=865196 for anyone interested.
2013/4/23 Ron Buckton <rbuckton at chronicles.org>
I fall mostly under the "native futures should not implicitly chain library futures" camp. Its easy enough to write:
var p = // some thenable return new Future(resolver => p.then(resolver.resolve, resolver.reject);
That looks terrible inefficient, so in our implementation we tried to make chaining easier by returning a new promise from then() based on promise.constructor. And once in the wild the issues started appearing like someone trying to implement a LazyPromise and having issues chaining then(). So we're considering reverting to returning a simple promise before it becomes technical debt.
Juan
On Tue, Apr 23, 2013 at 10:38 PM, Brendan Eich <brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
I still think that burdening the call-site with the need to pull a future out of the object like that (even though, yes, the object itself will be optimized out by future VMs) is too much.
This started (for me) when you wrote:
It would be so nice if JS had multiple return values, so we could let cancellable future-returning APIs just return a naked resolver as their second value,
But now that I've pointed out JS has multiple return values, you're not happy :-P.
Like I said, destructing probably takes the wind out of multiple return values. The fact that you answered "Multiple return values?" with "Destructuring!" just confirms this. ^_^
Le 24/04/2013 19:41, Andreas Rossberg a écrit :
On 24 April 2013 19:20, Mark S. Miller <erights at google.com> wrote:
On Wed, Apr 24, 2013 at 10:14 AM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
Q and similar libraries don't actually assume that a Future<Future<x>> is a Future<x>. Yes it does. Except of course that we call these "promises". Please see the extensive discussions on the Promises/A+ site about why this flattening behavior is important. That strikes me as a very odd design decision, since it would seem to violate all sorts of structural and equational invariants. From a developer perspective, my experience using promises is that it's an extremely convenient property. Basically, if you can only have x and Future<x>, but never Future<Future<x>>, you never have to worry about
the resolved value. You know it's always a non-Future value. And that's what you want. When you call getQueryResult(query).then(function(result){...}), you always want result to be something you can play with, not a promise. Unless you're writing the promise infrastructure, you don't want to have to worry about that.
If Future<Future<x>> can exist, then you'll have to write this
boilerplate code in a lot of places: f.then(function res(v){ if(Future.isFuture(v)){ v.then(res); } else{ // actual work with the resolved value. } })
The promise library taking care of this boilerplate for you is a good property in my opinion. It also helps making chaining more usable and ease refactoring:
var p2 = p.then(function(x){
return somethingWith(x);
})
Whether somethingWith(x) returns a promise or non-promise value, you know that in p2.then(), you'll be dealing with a non-promise value. If, for whatever reason, you change somethingWith(x); to return a promise instead of a non-promise (or vice-versa), none of your code (beyond the somethingWith body) needs to be changed (assuming, you're only returning the value as it's the case here). I've had only once to change the signature of a function from a non-promise to a promise and that property was really useful.
As soon as you have a decent code base with a lot of functions returning and playing with promises, it's nice to not have to worry about "a promise for a promise" and have the promise infrastructure doing the flattening work for you.
On Tue, Apr 23, 2013 at 11:30 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Tue, Apr 23, 2013 at 11:25 AM, David Sheets <kosmo.zb at gmail.com> wrote:
On Tue, Apr 23, 2013 at 7:02 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Tue, Apr 23, 2013 at 6:12 AM, David Bruant <bruant.d at gmail.com> wrote:
Anyway, my point was that there exist libraries in which "then" is a function and the object with this function isn't a promise. These libraries will end up in a terrible confusion when used with Futures. You think you're resolving a future with an object and... oops! the built-in Future algorithm confused your object for a promise. Suddenly, not only are you not resolving your promise with the value, but your .then method is called unexpectedly.
Agreed. I'll note, though, that if the semantics of .resolve() are non-recursive (it strips off one layer of promise/future, but no further), then this becomes somewhat less of an issue, as it can be worked around - if you want to return something that's not a promise, just wrap it in a fulfilled promise (via some sugar function - I've proposed such in www-dom). That wrapper will be unwrapped straight away, and the future accepted with the value within.
lists.w3.org/Archives/Public/www-dom/2013AprJun/0051.html? Future.of == "monadic return"? Doesn't seem too sugary...
It's sugar because you already achieve it with existing functionality. Instead of "var f = Future.of(v);", you can write:
var f = new Future(function(r) { r.accept(v); });
or, in ES6:
var f = new Future(r=>r.accept(v));
However, I think it's sufficiently useful sugar that it bears adding.
And yes, it's the monadic return.
The big problem only arrives if you both treat all thenables as promises/futures, and have recursive resolve semantics, because then you can't ever return a non-promise with a .then() method.
I'd prefer that resolve only strip away a branded future, not an arbitrary thenable, and for there to be a way to brand a thenable as a future. That way, using other-library promises just requires one trip through the branding function and they can interoperate. Existing libraries tend to expose the same mechanism already, so that the other-library promises expose all the proper methods of the "main" library.
But I want non-recursive resolve more, and if I was forced to choose one, would take monadic resolve over branded futures.
"non-recursive resolve" == "monadic run" == "comonadic extract"? "recursive resolve" == "monadic run" compose { fixpoint of "monadic join" }?
Yes. (Comonadic "extend", not "extract".)
Will Futures be associative and have left and right identity between bind ("then") and return ("of")? Will this be specified explicitly?
Yes, they do. It's part of the basic mechanics of the two functions.
Domenic points out lists.w3.org/Archives/Public/public-script-coord/2013AprJun/0234.html
that DOM Futures currently do a recursive resolution which seems to violate these equivalences. I also don't see anything about these equivalences specified explicitly.
Notably, it seems that DOM Futures is written in such a way as to abstract over resolvers though I don't see how to provide an author-defined FutureResolver. Can I simply construct a JavaScript object that quacks like FutureResolver and use the FutureInit constructor?
Thanks,
On Fri, Apr 26, 2013 at 9:01 AM, David Sheets <kosmo.zb at gmail.com> wrote:
Domenic points out lists.w3.org/Archives/Public/public-script-coord/2013AprJun/0234.html that DOM Futures currently do a recursive resolution which seems to violate these equivalences. I also don't see anything about these equivalences specified explicitly.
I find it very hard to follow the DOM Future spec explicitly. However, when I asked Anne directly, he told me the opposite - that a nested future would only do single-unwrapping. Of course, I may have asked it in a confusing manner, but I tried my best to be very explicit and clear.
I've just now asked him again, and he confirmed it. I also just traced through the spec, and am pretty certain that he's right - Future#then is monadic.
Notably, it seems that DOM Futures is written in such a way as to abstract over resolvers though I don't see how to provide an author-defined FutureResolver. Can I simply construct a JavaScript object that quacks like FutureResolver and use the FutureInit constructor?
(Note: this is a tangent. Not that that's bad, but anyone responding to the previous paragraph probably doesn't need to respond to this paragraph.)
FutureResolver is intimately tied to Future. You don't want to provide your own, because then it won't have any tie to the Future's internal state. The spec does some funky gymnastics that make it look like it's constantly referring to the resolver, but that doesn't mean you need to provide your own resolver - that just lets it automatically deal with multiple callbacks registered on the same future, etc.
The current DOM spec, in code:
Future.resolve(1).then(value => {
value === 1;
// Return a non-promise
return 2;
}).then(value => {
value === 2;
// Return a promise for a non-promise
return Future.resolve(3);
}).then(value => {
value === 3;
// Return a promise that *resolves* to a promise
return Future.resolve(Future.resolve(4));
}).then(value => {
value === 4;
// Return a promise *whose value* is a promise
return Future.accept(Future.resolve(5));
}).then(value => {
value !== 5;
// Return the value-which-is-a-promise
return value;
}).then(value => {
value === 5;
});
As a gist: gist.github.com/zenparsing/5469130
Please let me know if I made a mistake with the above.
On Fri, Apr 26, 2013 at 11:03 AM, Kevin Smith <zenparsing at gmail.com> wrote:
The current DOM spec, in code:
Or, much more simply:
- Future.accept takes a Foo and returns a Future<Foo>.
- Future.resolve takes either a Foo or a Future<Foo>. In either case,
it returns a Future<Foo>.
- In Future.then(cb), the cb is expected to have the signature (Foo ->
Future<Foo>). It has some extra magic that allows you to return a
plain Foo, and treats it the same as if you returned a Future<Foo>.
Effectively, this means that it passes the return value through Future.resolve() to normalize it into a Future<Foo>.
- You can totally make nested Futures deliberately by doing something like "Future.accept(Future.accept(5))".
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => {
console.log(value !== 1);
return Future.accept(Future.resolve(1));
}).then(value => {
console.log(value === 1);
});
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com> wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
Future.resolve(1) returns a Future<1>.
Future.accept(Future.resolve(1)) returns Future<Future<1>>.
If you call .then() on a Future<Future<1>>, the callback receives a
Future<1> as its argument.
If you return a Future<Future<1>> from the callback, the chained
future adopts its state, which means that the chained future is now also a Future<Future<1>>.
So, calling .then() on the chained future will give you the same result - the callback receives a Future<1> as its argument.
(Using a mixture of Future.accept and Future.resolve in the way that you have makes things more confusing than necessary. When called on a plain value, the two functions are identical. They only act differently when you call them on a future.)
2013/4/26 Tab Atkins Jr. <jackalmage at gmail.com>
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com> wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
I think Kevin's assertion is correct. According to the spec, callbacks are
wrapped in something called a "future wrapper callback". When a promise is returned from the callback, the wrapper does this:
Let value be the result of invoking callback with argument as argument.
(...) run resolver's *resolve *with value and the synchronous flag set.
resolve tries to adopt the promise by being recursive, effectively flattening the promise:
- If value is a JavaScript Object, set then to the result of calling the JavaScript [[Get]] internal method of value with property name then.
- If JavaScript IsCallable(then) is true [treats all thenables the same way], run these substeps and then terminate these steps:
- Call the JavaScript [[Call]] internal method of then with this value value and *resolve *and *reject *as arguments.
If resolved called the thenable's then() with *accept *and reject, it
would only unwrap one layer.
Juan
On Fri, Apr 26, 2013 at 7:25 PM, Kevin Smith <zenparsing at gmail.com> wrote:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No. Per the definition of the then() method and dom.spec.whatwg.org/#concept-future-wrapper-callback then() will just be invoked with the result of the future. Future.accept() sets the result of the new future to the argument it was passed. Future.resolve() sets the result of the future to the resolved value (does unwrapping of a thenable).
If resolved called the thenable's then() with *accept *and reject, it would only unwrap one layer.
Good spec googles (I think). I just whiteboarded it and came to the same conclusion. Does anyone else have a prototype implementation they can run this program on?
On Fri, Apr 26, 2013 at 7:44 PM, Juan Ignacio Dopazo <dopazo.juan at gmail.com> wrote:
I think Kevin's assertion is correct. According to the spec, callbacks are wrapped in something called a "future wrapper callback". When a promise is returned from the callback, the wrapper does this:
Let value be the result of invoking callback with argument as argument. (...) run resolver's resolve with value and the synchronous flag set.
resolve tries to adopt the promise by being recursive, effectively flattening the promise:
If value is a JavaScript Object, set then to the result of calling the JavaScript [[Get]] internal method of value with property name then. If JavaScript IsCallable(then) is true [treats all thenables the same way], run these substeps and then terminate these steps: Call the JavaScript [[Call]] internal method of then with this value value and resolve and reject as arguments.
If resolved called the thenable's then() with accept and reject, it would only unwrap one layer.
Oops yeah. I guess that should be fixed. :/
2013/4/26 Anne van Kesteren <annevk at annevk.nl>
If resolve called the thenable's then() with accept and reject, it would
only unwrap one layer.
Oops yeah. I guess that should be fixed. :/
Fixing that would break compatibility with Promises/A+. To remain compatible with A+ and unwrap only one layer, the spec would need a way to discern promises from thenables.
Juan
Oops yeah. I guess that should be fixed. :/
Fixing that would break compatibility with Promises/A+. To remain compatible with A+ and unwrap only one layer, the spec would need a way to discern promises from thenables.
I don't think so. It has no bearing on Promises/A+, because A+ doesn't
test the case where the promise's value is itself a promise. Or to put it
another way, in A+ then
will never give you a promise.
I've made the change to my prototype and it still passes A+ (as well as passing that gist I linked to).
On Apr 26, 2013 8:33 PM, "Tab Atkins Jr." <jackalmage at gmail.com> wrote:
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com>
wrote:
Actually, I may have gotten it terribly wrong (apologies). In my
prototype
implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
Future.resolve(1) returns a Future<1>.
Future.accept(Future.resolve(1)) returns Future<Future<1>>.
This would all be easier to discuss if you weren't writing using invented methods.
2013/4/26 Kevin Smith <zenparsing at gmail.com>
Oops yeah. I guess that should be fixed. :/
Fixing that would break compatibility with Promises/A+. To remain compatible with A+ and unwrap only one layer, the spec would need a way to discern promises from thenables.
I don't think so. It has no bearing on Promises/A+, because A+ doesn't test the case where the promise's value is itself a promise.
Yes, sorry. It will on version 1.1: promises-aplus/promises-spec/#the-promise-resolution-procedure
Juan
On Fri, Apr 26, 2013 at 12:24 PM, Alex Russell <slightlyoff at google.com> wrote:
On Apr 26, 2013 8:33 PM, "Tab Atkins Jr." <jackalmage at gmail.com> wrote:
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com> wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
Future.resolve(1) returns a Future<1>.
Future.accept(Future.resolve(1)) returns Future<Future<1>>.
This would all be easier to discuss if you weren't writing using invented methods.
I'm using the methods defined in the Futures spec, because we're talking about the behavior of Futures.
From: Juan Ignacio Dopazo [dopazo.juan at gmail.com]
2013/4/26 Kevin Smith <zenparsing at gmail.com>
Oops yeah. I guess that should be fixed. :/
Fixing that would break compatibility with Promises/A+. To remain compatible with A+ and unwrap only one layer, the spec would need a way to discern promises from thenables.
I don't think so. It has no bearing on Promises/A+, because A+ doesn't test the case where the promise's value is itself a promise.
Yes, sorry. It will on version 1.1: promises-aplus/promises-spec/#the-promise-resolution-procedure
To clarify: in 1.0, the behavior of returning a thenable was highly underspecified, in part because of a lack of clarity about promises vs. thenables. In 1.1, returning a thenable is now specified in the same detail as the rest of the spec, and (of course) has accompanying tests.
Yes, sorry. It will on version 1.1: promises-aplus/promises-spec/#the-promise-resolution-procedure
To clarify: in 1.0, the behavior of returning a thenable was highly underspecified, in part because of a lack of clarity about promises vs. thenables. In 1.1, returning a thenable is now specified in the same detail as the rest of the spec, and (of course) has accompanying tests.
Ah - thanks for the info. But as far as I know, you can't test the case where a promise's value is actually a promise, because Q doesn't support that feature. True?
The difference would only surface when testing that case.
From: Kevin Smith [zenparsing at gmail.com]
Yes, sorry. It will on version 1.1: promises-aplus/promises-spec/#the-promise-resolution-procedure
To clarify: in 1.0, the behavior of returning a thenable was highly underspecified, in part because of a lack of clarity about promises vs. thenables. In 1.1, returning a thenable is now specified in the same detail as the rest of the spec, and (of course) has accompanying tests.
Ah - thanks for the info. But as far as I know, you can't test the case where a promise's value is actually a promise, because Q doesn't support that feature. True?
Correct.
The difference would only surface when testing that case.
Not quite. You can still have thenables that call onFulfilled
with another thenable, to any depth. You can also insert a real promise at the end of any-length thenable chain. A simple test case would be
const a = { then(onFulfilled) { onFulfilled(b); } };
const b = { then(onFulfilled) { onFulfilled(5); } };
adapter.fulfilled() // a real Q promise, via `Q.resolve()`.
.then(function () {
return a;
})
.then(function (value) {
assert.strictEqual(value, 5); // note: `5`, not `b` (and of course not `a`).
done();
});
I am also considering adding optional test cases for libraries that do expose a non-resolve fulfill, which Q would not partake in but e.g. DOMFuture could.
The difference would only surface when testing that case.
Not quite. You can still have thenables that call
onFulfilled
with another thenable, to any depth. You can also insert a real promise at the end of any-length thenable chain.
I understand now. Essentially, in the test you create an ad-hoc "promise"
whose value is a promise, by virtue of it calling its then
callback with
another promise.
So A+ 1.1 does present an opinion on then
semantics for
promises-of-promises.
FWIW, I think the DOMFuture behavior is more intuitive in this particular edge case. I also think that promises-for-promises are an edge case best avoided at all costs.
On Fri, Apr 26, 2013 at 11:44 AM, Juan Ignacio Dopazo <dopazo.juan at gmail.com> wrote:
2013/4/26 Tab Atkins Jr. <jackalmage at gmail.com>
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com> wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
I think Kevin's assertion is correct. According to the spec, callbacks are wrapped in something called a "future wrapper callback". When a promise is returned from the callback, the wrapper does this:
Let value be the result of invoking callback with argument as argument. (...) run resolver's resolve with value and the synchronous flag set.
resolve tries to adopt the promise by being recursive, effectively flattening the promise:
If value is a JavaScript Object, set then to the result of calling the JavaScript [[Get]] internal method of value with property name then. If JavaScript IsCallable(then) is true [treats all thenables the same way], run these substeps and then terminate these steps: Call the JavaScript [[Call]] internal method of then with this value value and resolve and reject as arguments.
If resolved called the thenable's then() with accept and reject, it would only unwrap one layer.
Gah, you're right.
So, this is a problem. The current behavior is incoherent wrt types, and is inconsistent between its first and second callbacks.
First, incoherency.
Given a Future<a>, its .then() method accepts two callbacks, each of
which is supposed to have the signature "a -> Future<b>". We apply
some magic to this so that you can also use functions of type "a -> b" (where "b" is not a Future) and we auto-massage it into working correctly. So far so good - this lets us chain Futures, which is great.
Now, if you omit one of the callbacks, we need to supply a "default" behavior. This needs to match the signatures above. In the Futures spec, both Future.accept() and Future.reject() have the signatures "a -> Future<a>", which is exactly what we need. Future.resolve(), on
the other hand, has the signature "(a or Future<a>) -> Future<a>",
which is inconsistent with the above. Signatures like this can be useful, so that calling sites can pass in both plain values and Futures, but it doesn't match the required signature for the .then() callbacks, and so it's unacceptable for a default behavior.
Second, inconsistency.
As I explained above, the signature of Future.reject() is "a ->
Future<a>". The signature of Future.resolve() is "(a or Future<a>) ->
Future<a>". The fact that these two signatures are different means
that omitting a callback will have observably different behavior based on which one you omitted.
For example, in the case of "Future.accept(Future.accept(5)).then().then(alert)", it will alert "5", because the resolve semantics for the missing callback in the first .then() will perform extra unwrapping of the value. (In other words, it gives you a different result than just doing "Future.accept(Future.accept(5)).then(alert)", which will alert "<object Future>" or whatever.)
On the other hand, in the case of "Future.reject(Future.reject(5)).then().then(null, alert), it will alert "<object Future>". (And you'll get the same behavior if you
remove the intermediary ".then()".)
This sort of inconsistency is bad news for authors trying to use Futures, as it means that the exact path the value took through accept/reject callbacks will change the value that it gets.
I have an implementation in Typescript/ES5 at rbuckton/promisejs/tree/master/Variations with a test suite that can be run from node.
Ron
Sent from my Windows Phone
On Friday, April 26, 2013, Tab Atkins Jr. wrote:
On Fri, Apr 26, 2013 at 12:24 PM, Alex Russell <slightlyoff at google.com<javascript:;>> wrote:
On Apr 26, 2013 8:33 PM, "Tab Atkins Jr." <jackalmage at gmail.com<javascript:;>> wrote:
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com<javascript:;>
wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
Future.resolve(1) returns a Future<1>.
Future.accept(Future.resolve(1)) returns Future<Future<1>>.
This would all be easier to discuss if you weren't writing using invented methods.
I'm using the methods defined in the Futures spec, because we're talking about the behavior of Futures.
Ugg...sorry. I wasn't aware the API had grown this much since I last looked at it. If there is consensus about the new methods, so be it.
On Sat, Apr 27, 2013 at 1:59 AM, Alex Russell <slightlyoff at google.com>wrote:
On Friday, April 26, 2013, Tab Atkins Jr. wrote:
On Fri, Apr 26, 2013 at 12:24 PM, Alex Russell <slightlyoff at google.com> wrote:
On Apr 26, 2013 8:33 PM, "Tab Atkins Jr." <jackalmage at gmail.com> wrote:
On Fri, Apr 26, 2013 at 11:25 AM, Kevin Smith <zenparsing at gmail.com> wrote:
Actually, I may have gotten it terribly wrong (apologies). In my prototype implementation, the following:
Future.accept(Future.resolve(1)).then(value => { console.log(value !== 1); return Future.accept(Future.resolve(1)); }).then(value => { console.log(value === 1); });
logs
- true
- true
Is that what it should be doing, according to the DOM spec? Anne, Alex?
No, it should be "true", then "false".
Future.resolve(1) returns a Future<1>.
Future.accept(Future.resolve(1)) returns Future<Future<1>>.
This would all be easier to discuss if you weren't writing using invented methods.
I'm using the methods defined in the Futures spec, because we're talking about the behavior of Futures.
Ugg...sorry. I wasn't aware the API had grown this much since I last looked at it. If there is consensus about the new methods, so be it.
There is not. Neither is there consensus about the old ones.
Mark: It's also unfortunate and incorrect to say "the w3c forked this". This plan was fleshed out on public-script-coord and you've been part of the evolution of the proposal ever since. I don't understand what, if anything, you're objecting to.