Promise resolution "helpfully" does different things depending on whether the return value of resolve() or the return value of the function passed to then() is a promise or not.
Therefore, understanding and predicting the behavior requires knowing precisely what criterion is being used (or is allowed to be used) to determine whether something "is a promise" as I use the phrase in the above sentence, so I'd like to nail that down. I'm interested in Promises/A+ in general, and ES6 native promises in particular.
Looking to https://promisesaplus.com/, it says:
1.1 "promise" is an object or function with a "then" method
whose behavior conforms to this specification.
To see what that means, I look further into 2.2 "The then method", and find that it rests on a set of behavior criteria making it clearly impossible to decide algorithmically whether a given object is a promise or not (proof by Halting Problem). And that's fine; it just means the spec of the "helpful" behavior in question won't be using the term "is a promise" directly.
So, looking further for the spec of the "helpful" behavior, I find 2.3 "The Promise Resolution Procedure". Surprise! It does use the term "is a promise" directly:
2.3.2 If x is a promise, adopt its state [3.4]
But it saves itself from a descent into meaninglessness in the footnote:
[3.4] Generally, it will only be known that x is a true promise
if it comes from the current implementation. This clause allows
the use of implementation-specific means to adopt the state of
known-conformant promises.
In other words, 2.3.2 didn't really mean "If x is a promise", it really meant (and should have said, IMO) "If x is known to be a promise".
But, if I understand correctly, that part is just a shortcut it's allowed to take if it can prove it's safe to do so. Moving on, the relevant section seems to be 2.3.3, which I summarize as: treat the return value x like a promise iff x has a property named "then" which is a function.
So then it all rests on the definition of "x.then is a function".
What does that mean, precisely, for the purposes of someone wanting to implement a conformant library,
or someone wanting to predict what a conformant library must/might do when I use it?
Is it the same as saying typeof x.then === "function"
?
Hoping for more clues, I looked into the spec of a supposedly-conformant implementation (or set of implementations), ES6 native promises, which is linked from the Promises doc on MDN. I believe the relevant section is 25.4.1.3.2. It looks like the criterion in question is IsCallable(x.then); following the link to that, I see to my dismay that IsCallable is not an actual function but rather an "abstract operation", defined in terms of many other "abstract operations" which are far from simple.
At this point, my hope of seeing any conformant reference code which implements the decision in question appears to be rapidly receding :-(
Backing up to my original question "How does promise resolution decide whether to treat a return value as a promise?", I think I've boiled it down, as I explained above, to the more specific question: Exactly what does the Promises/A+ spec mean when it says "is a function"?
If that has a simple clear answer (which of course might allow room for variation among implementations), immediate follow-up questions would be:
- Does native promises conform to the simple clear answer?
- Why is native promises' implementation of promises/A+'s "is a function" so complicated?
- What's a reasonable way for me to ensure that my return value will be treated as a promise as I intend, or as a plain value as I intend?