-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Currently the prolyfill uses this code for testing whether a value is a Future:
!!x && typeof x.then === 'function' && x.then.length === 2The issue with "duck testing" is that, as @domenic raised in Issue #13, the type of a fulfilled future is not fully polymorphic: you must never fulfill with a future that you intended to be a completed value. Bottom line: there needs to be a super-clear and ideally super-simple definition of what it means to be a future.
I'll pitch a few alternatives here, but I make no claim to exhaustiveness. Other suggestions welcome.
Approach: a "Future" [[Class]] with an isFuture test
Using instanceof is traditionally brittle because when multiple frames interact, they can have multiple, mutually incompatible versions of a given "class." That's why ES5 introduced Array.isArray as a way of testing whether something has the [[Class]] "Array", which is robust across frames, even when they have different Array constructors. So an analogous approach would be to introduce a new "Future" [[Class]] and an isFuture test that inspects the class. This is very clear and very robust, but has the downside that you can't create futures conveniently with an object literal.
Approach: a Symbol to definitely brand futures
Another approach would be to create a unique symbol (a new ES6 feature for creating opaque unforgeable property keys) that's shared in the standard library and identical across all frames. Then to create a future you would have to have this symbol, a la:
var myFuture = {
[futureSymbol]: true,
then: function(/* ... */) { /* ... */ },
// ...
};Approach: a string key to "probabilistically" brand futures
A lower-tech approach is to have some sort of published key that is simply a standard string key, but perhaps distinctive enough that it's highly unlikely (though still possible) that someone could accidentally create one. For example, Promises/A+ discussed alternatives like p.then.aplus or p.isPromise.
This approach is certainly the easiest, but I would hope we could do better when we have the opportunity to define this in the web platform and/or ES standard. What's particularly galling about it is that it effectively declares that whatever public key it's using is once and for all permanently associated with this type—that's global pollution of the entire JavaScript language. Scumbag futures!
Approach: type-and-arity check (again, probabilistic)
We could always just do what the prolyfill currently does. Again, while it's unlikely that someone would accidentally fulfill a value that is not intended to be a future but looks like one, that doesn't mean it can't happen. For example, you might be writing future-aware code that nevertheless fulfills with the result of calling some third-party code, and passes it back over to some other third-party code. And those third-parties may not be future-aware. If they happen to be using values that look like futures, not only will the program be buggy, it'll be buggy in badly unpredictable ways.
See also:
- Small clarification on what is a legal value promises-aplus/promises-spec#44
- Issue Futures-for-futures #13
- https://github.com/slightlyoff/DOMFuture/blob/master/polyfill/src/Future.js#L83
Dave