Killing `Promise.fulfill`

Tab Atkins Jr. jackalmage at gmail.com
Thu Aug 22 09:13:49 PDT 2013


On Thu, Aug 22, 2013 at 8:04 AM, Mark S. Miller <erights at google.com> wrote:
> You're right, my code is wrong. What I meant is:
>
>       Object.create(p, {Promise: {value: Promise}}).Promise(....)
>
> In other words, the Promise constructor might get supplied as its "this" an
> object that passes "p instanceof Promise" for innocent reasons other that
> "new" or .call. The question is, what does the Promise constructor test in
> order to determine whether it should use its coercion behavior or
> constructor behavior. If the test were "p instanceof Promise", then the
> above call, which was clearly intending to invoke its coercion behavior,
> would accidentally invoke its constructor behavior instead.

Okay, that's still defending a user from themselves, but whatever, I'm
fine with that.

> In any case, postponing subclassing till ES6 when we have the needed
> support, I think I know how to "solve" the problem. It is a bit weird.
>
>     var Promise = (function(){
>         "use strict"; // of course
>
>         var brand = new WeakMap();
>
>         // only ever called with "new"
>         function HiddenPromiseConstructor(callback) {
>             // initialize "this", which it can assume starts fresh and
> trustworthily uninitialized
>             brand.set(this, true);
>         }
>
>         function Promise(arg) {
>             if (Object.getPrototypeOf(this) === Promise.prototype &&
> !(brand.has(this))) {
>                 // assume likely called with "new", but do not trust "this"
>                 return new HiddenPromiseConstructor(arg)
>             } else {
>                 // assume called for coercion behavior. Ignore this
>                 if (brand.has(arg)) {
>                     return arg;
>                 } else {
>                     return Promise.of(arg);
>                 }
>             }
>         }
>         HiddenPromiseConstructor.prototype = Promise.prototype;
>
>         // initialize Promise.prototype
>         // initialize Promise statics
>         return Promise
>     })();

There's an even easier method.  Using "new" doesn't do anything
magical, it just sets `this` to be a fresh object with the right
proto.  We can do that ourselves, and you can return whatever object
you want from the constructor, so you can avoid the
HiddenPromiseConstructor by just using an Object.create() call:

   var self = Object.create(Promise.prototype);

Put that at the top of your constructor code, and return it at the
end, rather than `this`.  Use whatever method you feel like for
determining that you were called as a constructor.

Otherwise, yeah, your code is how to do it, until we get the ability
to specifically respond to being called vs being constructed.

~TJ


More information about the es-discuss mailing list