[strawman] Symbol.thenable proposal

Tab Atkins Jr. jackalmage at gmail.com
Mon Apr 16 17:36:16 UTC 2018

On Fri, Apr 13, 2018 at 6:00 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
> I can't remember where, but I recall seeing this discussed elsewhere
> (maybe in the TC39 meeting notes?) and the conclusion was basically
> ¯\_(ツ)_/¯. I'm not convinced myself it's actually worth the extra
> symbol just to make something not considered a thenable - all these
> Promise libraries have been able to get away with it for this long;
> what makes ES promises any different here? (Dynamic import is probably
> the only possible case I can think of short certain proxies in terms
> of things that could be considered thenables but shouldn't always.)

Having a reserved property key is a big footgun; you *can't* reliably
resolve a promise to an arbitrary object, because if the object
happens to have a "then" key, it'll try to recursively resolve it.

Userland may "get away with it" because "then" isn't a common key, but
it's still a hassle, same as how __proto__ being a special key in JS
causes problems with JSON usage - it's rare, but not unknown, and
problematic because it's an obvious layering violation.

> Worst case, you can just return a value that happens to have a promise
> in a property, like in `{value: somePromise}` - nobody resolves that
> except `co` IIRC.

Note that this isn't about nesting promises (or even promise-likes)
without recursive resolution, it's about nesting *thenables* into a
promise without (attempted) recursive resolution, which is a much
larger conceptual class: the presence of a "then" property says
absolutely nothing about the type of an object. We've just been making
a statistical judgement about the posterior probability of an object
being promise-like based on the presence of a particular key, which is
very dubious.

I'm still strongly of the opinion that we messed this up; the reason
we went with thenables was because it's how userland did it, and they
were working under the constraints of a proliferation of promise-likes
and the difficulty of userland type-testing; real Promise usage
promulgated much quicker than the pessimistic estimates, tho, making
the relative trade-off of "free" compatibility with promise-likes vs
the layering violation of reserving a property name on every object
forever much less favorable.  We should have instead relied on the
actual Promise type, with a Symbol escape-hatch opting a userland
object into being a promise-like.  Doing the reverse and letting
objects opt *out* of being treated as promise-like is probably the
most we can do at this point, unfortunately. Generic data-handling
will just have to manually add such a Symbol to objects they're
resolving to, which sucks but is better than the alternative of always
using a wrapper object.

(Note that having a "strictResolve" method won't help; it might
prevent the precise promise you construct from recursing into the
object, but if you then resolve *another* promise to that promise,
it'll go ahead and try to recurse again.  Maybe strictResolve() could
auto-add the "I'm not a promise-like" symbol to the object it resolves
to? That would be a bit more ergonomic for generic handling.)


More information about the es-discuss mailing list