Ah, wouldn’t it be nice if…

let data = await new NetworkRequest(url)

Simple, right?

class NetworkRequest extends Promise { ... }

Nope.

It makes sense, after all. Creating a subclass with a different constructor signature is a pretty normal thing to do. But changing the constructor would imply changing then as well, since then creates a new promise.

It turns out the above doesn’t really need to modify anything about the behavior of the promise chain, just kick it off differently. The solution is to add a then method to a non-promise object that forwards its arguments to a vanilla promise. The promise should be created prior to entering then, otherwise the idempotent expectation for then is broken. This works with async/await too since they just want a thenable.

The example class below, ValueInTime, is a promise for a value in some number of seconds.1 Its constructor takes a value and a number of seconds. Its then method is just a simple forwarder as described above.

subclass_not.js
// Subclass a promise to have a different constructor?
// Don't subclass. Compose!
class ValueInTime
{
    constructor (value, time)
    {
	this.promise = new Promise ((resolve, reject) => {
	    setTimeout(() => resolve(value), time * 1000)
	})
    }

    then ()
    {
	return this.promise.then.apply(this.promise, arguments)
    }
}

async function test ()
{
    for (let n=0; n<3; ++n)
	console.log(await new ValueInTime(n, 1))

    return new ValueInTime('done', 1)
}

test().then(console.log)

Using this technique it’s possible to:

new NetworkRequest(url).then(process).then(log).then...

… which is exactly what I was thinking (you too, admit it) when the “need” to “subclass” a promise arose. Yet another case of composition vs. inheritance.

If you really do want to extend Promise, see my next post on promises.

1
To enable async/await, start chrome with -js-flags="-harmony-async-await" or use babel.

See Also

Spec creates strict requirements on subclass constructors

Possible issue with subclassing

How to subclass a promise