W3C home > Mailing lists > Public > www-dom@w3.org > April to June 2013

Re: Deprecating Future's .then()

From: Sean Hogan <shogun70@westnet.com.au>
Date: Mon, 20 May 2013 11:04:35 +1000
Message-ID: <519976A3.9080105@westnet.com.au>
To: "Tab Atkins Jr." <jackalmage@gmail.com>
CC: "www-dom@w3.org" <www-dom@w3.org>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Anne van Kesteren <annevk@annevk.nl>
On 20/05/13 8:32 AM, Tab Atkins Jr. wrote:
> I *really* don't like this proposal.  Comments inline, as usual.
>
> On Sat, May 18, 2013 at 3:09 PM, Sean Hogan <shogun70@westnet.com.au> wrote:
>> BENEFITS:
>>
>> The main benefits of this addition are:
>>
>> a. consistency in the way futures are initialized and resolved - that is,
>> resolving is always asynchronous via `resolver.accept|reject`.
> This comes at the expense of easier and simpler code to write in the
> common case, when you're receiving an already-built future and are
> just acting on it via .then().  You can no longer just return a value
> like a normal function; you are forced to call `this.accept()`.


You can still do this - I haven't proposed changing the behavior of .then().


> Given that you have to reject the promise based on uncaught errors in
> the callback anyway (or else you lose an important quality of
> promises), `this.reject()` doesn't give you anything
> (`this.reject(val)` is identical to `throw val`, but longer).
>
> You've also removed the ability to do `this.resolve()`, for some
> reason.  This isn't explained at all in your proposal, which makes me
> assume that you don't understand what it's used for.  This means you
> can't force your promise to delegate to another promise, as you can do
> today.


The resolve algorithm is still part of the way .then() handles callbacks 
- I haven't suggested changing that.


> Finally, you're overriding `this` to be the future's resolver, thus
> ruling out any possibility of passing in class methods that want to
> refer to the class instance when processing the future's value.  It's
> unclear what the effect of this would be on hard-bound methods, as
> well.


Yes, I couldn't work out why the callback would want access to the 
resolver's future.
Could you give an example of that usage?

If it is a problem then the resolver could be given a `.future` property 
to reference the context future.


>> b. easily mix with non-Promise asynchronous code without creating yet
>> another a new Future
> Why is this a problem?  It's very simple code:
>
> f.then(function(val) {
>    var resolver, newf = new Future(function(r){ resolver = r; });
>    doAsyncStuff(resolver);
>    return newf;
> }
>
> Here's the code in your proposal:
>
> f.then(function(val) {
>    doAsyncStuff(this);
>    return;
> }


Actually I'm not proposing changing .then().

The example I was thinking of (which I put in my proposal) used the 
Async callback model:

         callAsyncFunction(value, function(err, result) { ... }


> Slightly simpler, but not significantly.  We could even paper over
> this with a convenience method that created a new Future and returned
> a promise/resolver pair, making the gap even smaller.
>
> There is a new Future getting created, but if all you do is return it
> immediately, it'll get GCed away when appropriate.


Yes, the difference is precisely the creation of a new Future. Every time.

I addressed this in my proposal but I'll say it a different way here:

As you say:
     .thenfu() can be implemented with .then() plus the creation of 
*another* new Future

The reverse is also possible and even simpler:
      .then() can be implemented with .thenfu() plus the "resolve" algorithm

So .thenfu is simpler internally and is simpler for mixing with 
non-Promise async code.

.then() is simpler for synchronous code and Promise objects, but...


>
>> c. `then` doesn't need to be the feature-test for Promise objects,
>> and doesn't need to be  a forbidden method of non-Promise objects.
> I don't understand how this is a result of your proposal.


A contrived example:

     var Data = {
         x: 1,
         y: 2,
         then: function() { }
     }

     Future.accept()
     .then(function() {
         return Data;
     })
     .done(function(value) {
         someExternalCodeExpectingData(value);
     });

This chain will stall (silently) because `Data` will be treated as a 
Promise object.
Replace the .then() call with .thenfu and the following won't stall:

     .thenfu(function(value) {
         this.accept(Data);
     })


Personally:
- I want my async code to look like async code;
- I don't like the idea of receiving some quantum "value-or-a-Promise";
- and if my code receives one then I want **my** code to be in charge of 
detecting it.

I doubt I'm the only one who thinks this way.

My proposal:
- suits the async coding pattern,
- adds two methods but **no** complexity to the spec, and
- to my knowledge doesn't compromise the current compatibility with JS 
Promise implementations.
Received on Monday, 20 May 2013 01:05:21 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 20 October 2015 10:46:20 UTC