Deprecating Future's .then()

Sean Hogan <[email protected]>
Newsgroups gmane.comp.web.dom.general
Message-ID <[email protected]>
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`.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.