Re: [promise] writing detailed spec of ProgressPromise & implementing it

On 08/08/2013 14:31, Domenic Denicola wrote:
> To be clear, I am trying to help the browser implementer community avoid the
> mistakes that we, the promise implementer community, have made in this regard.

Much appreciated!

> I
> truly believe that if the lessons of history are not learned in this case, then
> in a couple years ProgressPromise will be seen as a bad-practice vestigial part
> of the web platform, which is to be avoided in new APIs and which plagues
> existing APIs that use it with unnecessary complexity and limits their
> extensibility.

I'm all for learning the hard earned lessons from existing implementations, and 
avoiding unnecessary complexity and placing limits on extensibility. :)

> This is exactly the situation we are now in with user-space
> promise implementations.
>
> The web platform does not need more such APIs.
>
> *From:*Domenic Denicola [mailto:domenic@domenicdenicola.com]
> *Sent:* Thursday, August 8, 2013 09:24
> *To:* Yusuke Suzuki; www-dom@w3.org
> *Subject:* RE: [promise] writing detailed spec of ProgressPromise & implementing it
>
> I would strongly advise against conflating progress (ongoing notifications about
> a streaming async operation) and promises (one-and-done async operations). The
> promise implementer community in general, and the Promises/A+ organization in
> particular, has found this to be an extremely problematic line of reasoning, and
> one that does not mesh well with the existing semantics or syntax of promises.
> It has led to increased confusion from our users and a muddling of the
> underlying conceptual model, especially e.g. as it pertains to error handling.

Might this be related to the way that current implementations have passed 
progress events along promise chains until a promise with a progress handler is 
encountered? In the discussions that I've been party to at Mozilla we did indeed 
see that this approach could make progress events on promises very confusing and 
hard to work with, and introduce serious gotchas. Two of the issues that I recall:

1) If more than one ProgressPromise is chained then a single progress handler on 
the last promise is unlikely to be able to make much sense of the progress 
events. For example, say the first promise is for an XHR which defines progress 
as the number of octets received and has a known total, and the next promise in 
the chain defines progress as the number of files encountered while scanning a 
directory tree containing an unknown total number of files. First the handler 
would get a series of events for the XHR progress, then, once that had 
completed, a series of events for the directory scanning. It wouldn't 
necessarily have any meaningful way to distinguish between the two types or the 
transition between the two, or have a meaningful way to combine them.

2) Adding progress events to existing promises becomes extremely hazardous to 
existing code - as in, it risks breaking code that has chained promises with a 
single progress event at the end, since that handler will not likely behave well 
if it starts receiving a second set of unexpected progress events with different 
semantics.

So we have not been intending to implement ProgressPromise in a way that would 
pass progress events along a chain of promises. We're only considering allowing 
the addition of a progress handler directly on the promise that the progress 
events are targeted at.

We also considered that using .then() to add progress handlers causes issues. 
There's the issues of extensibility that you bring up, and there's the issue 
that content authors are then much more likely to expect progress events to be 
passed along promise chains.

Instead what we have been considering to allow progress events to be added is this:

[Constructor(ProgressPromiseInit init)]
interface ProgressPromise : Promise {
   // Returns the context object
   Promise progress(optional AnyCallback progressHandler);
};

(Note that progress() returns a Promise, not a ProgressPromise, which is again 
about not chaining progress events.)

JS libs and content authors can then still get access to progress in the 
calculation of the result of the promise, but only for a given promise by adding 
a handler specifically to that particular promise. Library functions can even 
chain multiple promises that produce progress events, but if the promise that 
they themselves return is to have progress events, they have to consciously 
think about the semantics of the various progress events of the various promises 
that they are wrapping, and what the semantics of a "total progress" event for 
the promise that they will return should be.

I'm not sure if this changes how you might look at progress events on promises. 
If not I'd really like to hear more about the issues that would remain with the 
approach that we have been considering.

By the way I view progress as metadata about the resolution of the promise 
rather than two parallel pieces of data.

See also below for one of our use cases.

> The fact that any progress additions to the promise API have no place in a
> shallow coroutine-based syntax enhancement of promises (e.g. C#’s await, or
> ES6’s yield) further strengthens our view.
>
> As such I would ask you to consider alternate APIs that do not involve awkwardly
> stuffing streaming async semantics into one-and-done promises. For example,
> whichever object vends promises could be an EventTarget emitting progress
> events, or could instead of vending promises directly vend { promise,
> progressEmitter } pairs.
>
> If this is part of the desire for a stream API, then I’d reiterate my earlier
> question to ask which API proposal is actually under discussion?

In the case of the discussions I've had, it's to add a function that will open a 
system directory picker and then return a promise that will resolve to a flat 
list of all the files (with relative paths) under the directory tree that the 
user picks. The intention would be that the content author could implement their 
own open-a-directory-picker UI and, as the file list is being built up, the 
promise would be getting progress events enumerating how many files had been 
added to the (not yet exposed) file list that will eventually be returned. That 
way the author can provide a visual indication to the user of progress. (For 
example, "xxx files processed", where xxx continually increases until done.)

Jonathan

> *From:*yusukesuzuki@google.com <mailto:yusukesuzuki@google.com>
> [mailto:yusukesuzuki@google.com] *On Behalf Of *Yusuke Suzuki
> *Sent:* Thursday, August 8, 2013 04:15
> *To:* www-dom@w3.org <mailto:www-dom@w3.org>
> *Subject:* [promise] writing detailed spec of ProgressPromise & implementing it
>
> Hi,
>
> We're planning to make ProgressPromise spec more detailed and implement it on
> Blink experimentally.
>
> ProgressPromise is planed to be used for some APIs, such as XMLHttpRequest /
> FileAPI.
>
> Please feel free to comment about this.
>
> Thank you,
>
> Yusuke Suzuki
>

Received on Thursday, 8 August 2013 16:21:50 UTC