Differences between Promise.prototype methods with regard to what constitutes what constitutes compatible receiver
Darien Valentine
valentinium at gmail.com
Fri Jul 20 11:29:34 UTC 2018
In `Promise.prototype.then`:
> 1. Let promise be the this value.
> 2. If IsPromise(promise) is false, throw a TypeError exception.
> [ ... ]
In `Promise.prototype.finally`:
> 1. Let promise be the this value.
> 2. If Type(promise) is not Object, throw a TypeError exception.
> [...]
In `Promise.prototype.catch`:
> 1. Let promise be the this value.
> 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
First, this means that only `then` requires the this value to be a Promise:
```js
for (const key of [ 'then', 'finally', 'catch' ]) {
try {
Promise.prototype[key].call({
then: () => console.log(`${ key } doesn’t brand check its this value`)
});
} catch (err) {
console.log(`${ key } does brand check its this value`);
}
}
// > then does brand check its this value
// > finally doesn’t brand check its this value
// > catch doesn’t brand check its this value
```
Second, note that `Invoke` uses `GetV`, not `Get`. Thus:
```js
for (const key of [ 'then', 'finally', 'catch' ]) {
try {
String.prototype.then = () =>
console.log(`${ key } casts this value to object`);
Promise.prototype[key].call('foo');
} catch (err) {
console.log(`${ key } doesn’t cast this value to object`);
}
}
// > then doesn’t cast this value to object
// > finally doesn’t cast this value to object
// > catch casts this value to object
```
On reflection, I think I see the logic to this:
- `Promise.prototype.then` ends up executing `PerformPromiseThen`, which
requires its first argument to be a native promise object.
- `Promise.prototype.finally` ends up executing `SpeciesConstructor`, which
requires its first argument to be an object.
- `Promise.prototype.catch` does neither.
However the inconsistency within this trio seems pretty odd to me. I
suppose I would have expected them all to be as constrained as the most
constrained method needed to be for the sake of uniformity, given that they
constitute a single API. Conversely, if the goal was for each method to be
exactly as lenient as is possible, then `finally` seems to be
over-constrained; it seems like `C` could have just defaulted to `Promise`
in cases where `SpeciesConstructor` wasn’t applicable, making it as lenient
as `catch`.
I wasn’t able to find prior discussion about this, though it’s a bit hard
to search for, so I may be missing it. Do these behaviors seem odd to
anyone else, or is it what you’d expect?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/85ed179d/attachment-0001.html>
More information about the es-discuss
mailing list