I’ll preface this with a disclaimer that I’m not directly involved with any of 
the standards discussions, TypeScript, or IE at Microsoft, but rather am 
expressing my interest as a producer and consumer of Promises/Futures while 
building cloud applications. I’ve published a few variations of Promise/Future 
libraries for JavaScript at http://blogs.msdn.com/b/rbuckton and a lot of my 
experience is biased towards previously having leveraged Task-based asynchrony 
in C#.

Despite my API proposal leaning towards the term Promise, I’ll use the term 
Future instead to align with the proposal for DOM when speaking about Promises 
or Futures in the general sense.

My version of the PromiseResolver provides resolve/reject methods and does not 
include an ‘accept’ method. My understanding of this is that 
FutureResolver#accept resolves the value explicitly, while 
FutureResolver#resolve hooks the Future#done method of the value if it is a 
future so that Future(A) for Future(B) is eventually resolved as B, not 
Future(B). I’m not sure I understand all the use cases for accept over resolve 
at this point, but have always preferred the resolve approach in all of my 
actual uses so far.

Progress notifications are a bit of a mixed bag for me. My first 
implementations of Future didn’t have them, but I had a number of instances in 
the C# world where I needed a way to update the Future consumer for the benefit 
of UI notifications. The recent discussion on EventStreams has me wondering if 
progress notifications could serve a similar purpose for Futures, where a 
progress notification could be triggered for each instance of an event in the 
stream, where resolve is triggered for single use events (like 
DOMContentLoaded), or when the event producer is signaling that it has 
concluded processing.

With Progress notifications relegated to a subclass, would chained Futures also 
be ProgressFuture instances? The benefit of having progress as an optional 
member of the Future class is that a chained Future could also enlist in 
progress notifications, but that is less of a concern if the Future created by 
a .then from a ProgressFuture is itself a ProgressFuture.

Not having progress can be mitigated somewhat by passing in a progress 
notification callback to the function that creates the Future, but chained 
descendants would not be aware of the progress and would have a more 
complicated task to write code that properly accepts progress handlers, and 
then we’re getting back to the callback/errback continuation passing that 
Futures are partially designed to replace.

Cancellation was an attempt to support something akin to Cooperative 
Cancellation as it exists in .NET’s implementation, as well as a means to 
‘unlisten’ or forget a Future if you no longer need its value. In the API 
proposal, by default cancel would only essentially remove a Future (and 
therefore its chained descendants) from receiving resolve/reject/progress 
signals from its antecedent. Cancellation also would allow the ability to 
prevent the resolve/reject/progress callbacks from executing in a later turn of 
the dispatcher to prevent the execution of now unneeded code.  It can also be 
used to abort a XHR request or shut down a WebWorker.

The second callback in the Promise constructor would be a means to provide user 
supplied cancellation logic, such a updating the UI in response to a cancelled 
pending operation.  I debated on whether it should be possible to also cancel 
the antecedent tasks from a chained descendent, and it is a very tentative part 
of the API.

In the .NET world, I would use a CancelationTokenSource and CancellationToken 
to provide cancellation, which serves several purposes. One is the ability to 
prevent the execution of the background Task before it starts (which is 
provided by adding Promise#cancel()). Second is the ability to perform some 
kind of user-defined cleanup logic in the event of cancellation (e.g. detach 
event handlers, abort an XHR, notify the UI, etc.). The third is the ability to 
track cancellation when in a background thread that might be running in a loop, 
however with the possible exception of Web Workers, this is unlikely to be 
required in traditional JavaScript programs that are single threaded and rely 
on a dispatcher/event-loop and don’t have the traditional concept of a Thread. 
CTS also allows the ability to aggregate multiple cancellation tokens when 
waiting on multiple parallel tasks, which is even less likely in JavaScript/ES.

Promise#cancel() in this respect can have an effect similar to 
EventStream#unlisten in that proposal.

That being said, a “CancellationToken” could be implemented by passing in 
another Future to the function that generates the Future you care about. 
Resolving the “cancellation” future could be used to abort an XHR, but not to 
cancel a task that is still waiting to be executed on the 
dispatcher/event-loop, as the .then() would likely execute in a different turn, 
unless it could be explicitly marked as synchronous.

The options argument provides additional optional named parameters for the 
then/done/catch/progress continuations that in effect make the “synchronous” 
flag in the DOMFutures something that the user can control. This is similar to 
the TaskContinuationOptions.ExecuteSynchronously enum value in .NET which can 
be used to optimize some continuations to execute synchronously when its 
antecedent is resolved or rejected to reduce the need to wait for another turn 
of the dispatcher/event-loop. This optimization is primarily defined for small 
function bodies to reduce overhead, and could be used to make 
cancellation-by-future more effective.

The reason options is expected to be an object/object literal is that this can 
be extended to add additional control over the resulting continuation. This 
could include the ability to prevent cancellation (in the event .cancel is 
supported with the antecedents argument), or the ability to only signal chained 
descendants if a future is rejected and not to forward resolve to those 
descendants. This also allows for future additions to the options in later 
versions without breaking consumers. In this vein, it could be useful to have 
an options argument for the Future constructor as well, although I haven’t yet 
had an occasion to need one yet.

Finally, the additional API definitions are convenience APIs for certain 
scenarios. By default, I expect both Promise.resolve and 
PromiseResolver#resolve to only hook the resolve/reject of a Promise from the 
same library. Calling Promise as a function (or adding a Promise.of static 
method) might be the only Promise ‘interop' to userland Future libraries, 
though I would almost prefer that no ‘interop’ between libraries for a DOM or 
ES version to exist, but rather would require explicitly creating a new Future 
and using its resolver to interoperate with the userland promise.

The Promise.any, Promise.every, and Promise.some methods are very similar to 
what is in DOMFutures, except that the current version of the DOMFutures spec 
leaves a few things unspecified that could be problematic for end users. 
According to the spec for Future.every, order of the resolved values is 
arbitrary, based on the order that the provided Futures are resolved. As a 
consumer of Future.every, the Array of resolved values should be in the same 
order as the futures that were provided to the method, to be able to 
distinguish which value belongs to which future.  This may or may not be in the 
polyfill, but it is not explicitly (or at least clearly) specified in the 
DOMFutures spec. The same can be said for the Array of errors in the 
Future.some API definition.

I added AggregateError as a tentative Error object as a means to provide a 
single Error object to use as the value for the reject handler, and have 
considered wrapping all non Error values passed to the reject method on the 
resolver into an Error object to set expectations for the consumer. That way, 
the argument to the reject callback is always recognizable as an Error, and it 
can be easier to test the argument to provide appropriate handling. For 
instance, without Error wrapping or AggregateError, I would have to result to 
duck typing or Array.isArray to determine whether the errors provided are the 
result of a single error or multiple errors from a call to Future.some. This 
is, again, inspired by the .NET AggregateException, though I would likely send 
the single underlying Error if the AggregateError would only contain a single 
error.

The remaining API’s are designed to help support await-style asynchronous 
development as possibly afforded by generators or any future addition of 
something like “await” into the language. To that end, static methods like 
Promise.yield() and Promise.sleep() can help to let the dispatcher/event-loop 
do other work in the middle of a long-running async function, or to pause for a 
period of time before continuing such as with animation. Promise.delay() is 
similar to sleep, but resolves with a value.

Promise.run() is close to setImmediate, where the result is the future value of 
the callback. In this case, Promise#cancel() is then effectively a call to 
clearImmediate. In a similar fashion, Promise.start() is roughly equivalent to 
setTimeout with its Promise#cancel() then synonymous with clearTimeout.

I’m not strongly tied to having progress, cancel, or the synchronous option, 
but do find that they provide a level of flexibility. Subclassing Future to 
provide this could make sense, but again I am concerned about ensuring the 
subclass prototype is somehow reused for chained dependents so that you don’t 
lose your .progress or .cancel if you do a .then before you return. The 
.yield/.sleep/.delay convenience methods are much more useful with yield or 
await.

I can understand Luke’s concern around the state properties, the only one I 
might push back on might be PromiseResolver#wasCanceled if Promise#cancel were 
to be supported to be able to test for cancellation if the future might be 
resolved in a later turn than it was created (such as in the onload event 
listener for an XHR). The properties on Promise itself are much less necessary 
and I’m not strongly tied to them.

One last thing not mentioned in my proposal, nor the DOMFutures spec, is 
dealing with Error#stack with respect to Futures or async methods. Since ES has 
no rethrow concept, the only way for a reject handler to pass the error to 
chained descendants is to throw the exception. This can then possibly 
negatively effect the content of Error#stack and can complicate debugging 
futures. I am still reading through the issues list for DOMFutures, so I 
apologize in advance if this is a topic that has already been covered.

I sincerely look forward to a standard implementation of Futures, and truly 
hope this can become part of ES. Some of the proposals for ES6 and later could 
likely benefit from Futures. The current proposal for module Loaders already is 
leaning towards both Node/CPS-like callback/errback arguments for Loader#load 
as well as something very Future-like in its argument to Loader#fetch (at least 
as far as of what I have been able to find online). It seems to me that both 
methods would be better served by Futures. Object.observe could be served by 
something like the EventStreams proposal as well.

Best regards,
Ron

Sent from Windows Mail

From: Alex Russell
Sent: ‎Friday‎, ‎April‎ ‎19‎, ‎2013 ‎4‎:‎58‎ ‎AM
To: Ron Buckton
Cc: Anne van Kesteren, Mark S. Miller, es-discuss, [email protected], 
Markus Lanthaler, Douglas Crockford, Norbert Lindenberg, Luke Hoban, Tony Ross, 
Adrian Bateman

Hi Ron,

Comments inline.

On Wed, Apr 17, 2013 at 7:35 PM, Ron Buckton 
<[email protected]<mailto:[email protected]>> wrote:
As someone who has been interested in Promises/Futures in JavaScript for a 
number of years, I'd like to throw in my $0.02 regarding a proposed API for 
Promises/Futures for thoughts:

https://gist.github.com/rbuckton/5406451

My apologies in advance as the API definitions are written using TypeScript and 
not Web IDL.


There's a lot of API in here. If you give the DOMFutures github repo a look, 
you'll see that we considered some of them: 
https://github.com/slightlyoff/DOMFuture/

In particular, progress was explicitly written out of the contract of the base 
class so that subclasses that need it can mix it back in without burdening 
everyone else with perhaps nonsensical methods. See: 
https://github.com/slightlyoff/DOMFuture/blob/master/ProgressFuture.idl

As for the state variables, we've removed them in the most recent version to 
avoid potential for "cheating" (as Luke Hoban described it). I'm also not 
entirely sure I understand why the capability to cancel is being vended to all 
consumers of a Future. We explicitly disallow that in the current design to 
prevent the potential for multiple users of a Promise/Future stepping on each 
other. The thought with the current design is that if you have an interface 
that needs to vend cancelation, you should at it in a subclass of Future.

Your constructor signature looks similar (but not the same) as the one we ended 
up with, and there appear to be many convenience static methods on Promise. How 
strongly do you feel about them?

>From a design standpoint the only thing that jumps out at me as being very 
>strange is the "synchronous" option for .then() and .done(). What is it meant 
>to do?

-----Original Message-----
From: Anne van Kesteren [mailto:[email protected]<mailto:[email protected]>]
Sent: Wednesday, April 17, 2013 8:46 AM
To: Mark S. Miller
Cc: [email protected]<mailto:[email protected]>; Norbert 
Lindenberg; Markus Lanthaler; Douglas Crockford; es-discuss
Subject: Re: Futures (was: Request for JSON-LD API review)

> On Wed, Apr 17, 2013 at 8:29 AM, Mark S. Miller 
> <[email protected]<mailto:[email protected]>> wrote:
>> The main argument I've heard for proceeding with w3c/DOMFutures
>> rather than tc39/promises is that the DOM can't wait for tc39 to get
>> around to standardizing promises in ES7. But we should have our eyes
>> open to the consequences. As Crockford says (paraphrasing Knuth)
>> "Premature standardization is the root of all evil." The likely
>> result of DOMFuture proceeding in this way is that it will be wrong,
>> ES7 will be stuck with it and mostly unable to fix it, and we will
>> all be stuck with the consequences for a very very long time.
>>
>> As with Object.observe, if the need for promises is that urgent, it
>> needs to be on an accelerated track with the JavaScript context -- as
>> it already de facto is at promises/A+. It should not be needlessly
>> tied to the browser or to w3c.

I don't find the whole who owns what discussions very interesting to be honest. 
If it was up to me JavaScript would just be part of the W3C and we would not 
have to deal with that layer of distraction.

In any event, you can take the specification and improve on it elsewhere if you 
so desire. It is in the public domain for a reason.
You can also provide technical feedback as to what exactly is evil.
Saying "stop doing this" and implying you're somehow the superior forum to the 
other party is not helpful and has certainly not helped in the past.


--
http://annevankesteren.nl/

_______________________________________________
es-discuss mailing list
[email protected]<mailto:[email protected]>
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to