Differences between Promise.prototype methods with regard to what constitutes what constitutes compatible receiver
Jordan Harband
ljharb at gmail.com
Fri Jul 20 23:09:49 UTC 2018
This is intentional - `catch` delegates to `then`, so that a subclass that
overwrites `then` doesn't have to also override `catch` (and the same for
`finally`, which also calls into `then`).
On Fri, Jul 20, 2018 at 4:29 AM, Darien Valentine <valentinium at gmail.com>
wrote:
> 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?
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180720/4e6c8c48/attachment.html>
More information about the es-discuss
mailing list