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

Deprecating Future's .then()

From: Sean Hogan <shogun70@westnet.com.au>
Date: Sun, 19 May 2013 08:09:38 +1000
Message-ID: <5197FC22.7020704@westnet.com.au>
To: www-dom@w3.org, "public-script-coord@w3.org" <public-script-coord@w3.org>
CC: Anne van Kesteren <annevk@annevk.nl>
After more consideration I think I have some proposed changes which make 
Futures more consistent and flexible without compromising the current 
compatibility with JS Promise implementations.

PRIMARY CHANGE:

The primary change is the addition of two methods - call them `thenfu` 
and `catchfu` for now - which become the preferred methods for 
registering callbacks.

`thenfu` and `catchfu` are similar to `then` and `catch` in that they 
are called with "accept" and/or "reject" callbacks and that they return 
a new Future.

But they are also similar to the Future constructor in that the 
associated resolver is available to the callback (via `this`) as the 
intended means of resolving the future.


EXAMPLE:

Using `thenfu` might look like this:

     new Future(function() {
         this.accept(2); // NOTE: `resolver` is `this`
     })
     .thenfu(function(value) { // immediate "accept"
         this.accept(value * value); // NOTE: `resolver` is `this`
     })
     .then(function(value) { // same thing as previous `thenfu`
         return value * value;
     })
     .thenfu(function(value) { // a delayed "accept" doesn't require 
another new Future
         var r = this;
         setTimeout(function() {
             r.accept(value * 2);
         });
     })
     .thenfu(function(value) { // Using `thenfu` with some JS Promise
         var promise = getExternalPromiseDependingOnMyValue(value);
         promise.then(this.accept, this.reject);
     })
     .thenfu(function(value) { // mapping async callback model to Futures
         var r = this;
         callAsyncFunction(value, function(err, result) {
             if (err) r.reject(err);
             else r.accept(result);
         }
     })
     .done(function(value) {
         console.log(value);
     });


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`.

b. easily mix with non-Promise asynchronous code without creating yet 
another a new Future

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.


SECONDARY CHANGES:

This addition necessitates and / or enables some secondary changes, most 
notably:

a. the deprecation of `then` and `catch` from Future.

b. the removal of `resolve` from FutureResolver.

c. the modification of `new Future(init)` so that `resolver` becomes the 
`this` object of `init`.

d. the requirement for `resolver.accept` and `resolver.reject` to be 
bound methods, thus enabling them to work when called as bare functions, 
for example:

         jsPromise.then(resolver.accept);

e. It is also necessary to modify `Future.any|every|some` to not rely on 
`then` detection.
More on that in a future post.


INTEROPERABILITY:

The advice to JS libs and devs wanting to **mix DOM Futures and 
Promises** is:

1. `then` and `catch` are deprecated.

2. Future objects may be passed to code expecting Promise-like objects 
with a `then` method.

3. If you call a future's `then` or `catch` then your callback must 
return a Promise-like object - that is, an object with a compatible 
`then` method.

3. If you call a future's `thenfu` or `catchfu` then your callback must 
use the provided resolver and can accept or reject by any procedure.


ASIDE:

`then` and `catch` can be simply implemented on top of `thenfu` and 
`catchfu`. For example, some rough JS code for `catch` would look like:


     Future.prototype.catch = function(onreject) { // NOTE: assume 
onreject is guaranteed a function
         this.catchfu(function(value) {
             try {
                 var result = onreject(value);
                 if (typeof result === 'object' && typeof result.then 
=== 'function') {
                     result.then(this.accept, this.reject);
                 }
                 else this.accept(result);
             }
             catch(err) {
                 this.reject(err);
             }
         });
     }


The reverse is also trivial, but requires the creation of a new Future 
and tying it to the one already created by `catch`.
Received on Saturday, 18 May 2013 22:10:18 UTC

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