Differences between Promise.prototype methods with regard to what constitutes what constitutes compatible receiver
Jordan Harband
ljharb at gmail.com
Sat Jul 21 07:05:57 UTC 2018
That seems reasonable, although I'm not sure of the value of that
consistency. It'd have to be proven to be web-compatible - ie, if anyone
was adding a `.then` to a primitive prototype, they might be relying on the
current behavior of `.catch`.
On Fri, Jul 20, 2018 at 9:58 PM, Darien Valentine <valentinium at gmail.com>
wrote:
> That makes sense for sure, but I don’t think subclassing is impacted by
> receiver constraints in this regard either way, since subclasses will still
> be “IsPromise” promises. What is affected is portability of the methods to
> “non-IsPromise” thenables. Catch not requiring an IsPromise receiver makes
> sense from that angle:
>
> ```js
> OffbrandPromise.prototype.catch = Promise.prototype.catch;
> ```
>
> But if portability to non-native, non-Promise-subclass thenables is the
> goal, I’d still have expected finally to be the same “level” of generic as
> catch. Neither require their receiver to be an IsPromise-promise, but catch
> doesn’t even require its receiver to be an object, while finally does. This
> is a very minor thing obviously, but I wondered if it might still be web
> safe at this point to make catch require its receiver to be an object like
> finally does. It would be more consistent, and it’s pretty hard to imagine
> the current ability to call catch on a primitive, as in the second example
> above, being a useful behavior.
>
> On Fri, Jul 20, 2018 at 7:10 PM Jordan Harband <ljharb at gmail.com> wrote:
>
>> 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/20180721/317dc641/attachment-0001.html>
More information about the es-discuss
mailing list