Search code examples
javascriptecmascript-6es6-promise

How do await and Promise.resolve() test whether their arg is a thenable?


I would like to be able to reason about what await will do when it is called on various kinds of values.

The MDN documentation for await describes the decision procedure for that, one of the steps which is to decide whether the object is a thenable (after it has determined it isn't a Native Promise):

The expression is resolved in the same way as Promise.resolve(): it's always converted to a native Promise and then awaited. If the expression is a:

  • Native Promise [...]
  • Thenable object (including non-native promises, polyfill, proxy, child class, etc.): [...]
  • [...]

(And the linked doc for Promise.resolve() also says essentially the same thing-- in particular, it links to the same section on Thenable object in the Promise documentation).

So, following that link for Thenable object, still looking for a precise definition of Thenable object as it is used in the decision procedure (i.e. exactly how await and Promise.resolve() test for it, in that decision procedure):

A thenable implements the .then() method, which is called with two callbacks: one for when the promise is fulfilled, one for when it's rejected.

That's the closest thing I see to an answer on this page, but it's not an answer.

(E.g. is the thing, which is known to be a non-Native-Promise, considered to be a thenable if attempting to get its then property doesn't throw? Or, doesn't it throw and returns non-undefined? Or, doesn't throw and returns something whose typeof is function? Or, doesn't throw and returns something such that calling it with two callbacks doesn't throw? Etc.; there are lots of possibilities, and I'm sure this is nailed down in a spec somewhere.)

My precise question is: Exactly how do await and Promise.resolve() decide whether to treat their argument (once it's decided that it's a non-Native-Promise) as a Thenable object, for the purposes of the decision procedure quoted above?

(Note: this question is about the ES8 await operator and ES6 Promises; it isn't about bluebird or promises/A+ or any other promises implementation/spec, so previous questions/answers about those are not applicable.)


Solution

  • Is the thing considered to be a thenable iff attempting to get its then property doesn't throw and returns something whose typeof is function?

    Yes, very much that. You can find this specified in §27.2.1.3.2 Promise Resolve Functions, the procedure to resolve a promise with a value that is used by new Promise, Promise.resolve and (by extension) await. This still conforms to the Promises/A+ resolution procedure and its definition of "thenable".