4

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?
Don Hatch
  • 5,041
  • 3
  • 31
  • 48
  • 'Exactly what does the Promises/A+ spec mean when it says "is a function"?' - most definitely the same as ECMA specification - "member of the Object type that may be invoked as a subroutine" http://www.ecma-international.org/ecma-262/6.0/#sec-terms-and-definitions-function – weaknespase Mar 30 '16 at 18:19

1 Answers1

1

So then it all rests on the definition of "x.then is a function". What does that mean, is it the same as saying typeof x.then === "function"?

Yes, exactly that. Barring that accessing x.then throws, so you need to wrap all this in a try block.

Why is native promises' implementation of promises/A+'s "is a function" so complicated?

Because it's spec terminology. "function" is basically defined as a "callable object", and isCallable is just the algorithm to test for that. It's basically the same algorithm that the typeof operator uses to determine whether to return the string "function".


Btw, you'll be interested in Regarding Promises/A+ Specification, what is the difference between the terms "thenable" and "promise"?.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Are you saying IsCallable is equivalent to 'typeof x.then === "function"' ? I don't see anything in the three sections you referenced that states this. Can you give any supporting evidence in the ES spec saying they are the same, and/or evidence that 'typeof x.then === "function"' is the condition Promises/A+ spec intends to be talking about? Thanks for the link, yes that is interesting. – Don Hatch Mar 30 '16 at 17:58
  • @DonHatch: Yes, that's what I'm saying. I can't give further evidence that "*implements*" means the same as "*has the method*", and I can't cite any definitive reference that explains the wording of Promises/A+ any further, but I do *know* that this is what is intended. You can also check any of the promise implementations written in JS, or any polyfill that needs `isCallable` - they all use `typeof`. – Bergi Mar 30 '16 at 18:14
  • When you say you "know that this is what is intended", I assume you are talking about the Promises/A+ spec only (and not the ES question)-- in which case ok, that's believable, since the Promises/A+ spec is written in loose language. As for whether ES spec's isCallable is equivalent to "typeof ... === 'function'", I really have a hard time believing such an important equivalence without it being stated or clearly provable from the spec. I guess the fact that implementations use typeof is evidence, but if the spec doesn't even imply it, my god, what an absurd waste of words it would be. – Don Hatch Mar 30 '16 at 18:33
  • @DonHatch Actually you could let it refer to both, I do know lots of stuff :-) Every spec just establishes certain things by convention. Few documents state that they use the English language, or [what certain English phrases mean](https://tools.ietf.org/html/rfc2119). ES is not written in machine-readable language (where you could use a theorem prover to establish the equivalence), but this particular equivalence is pretty well-defined although not explicitly noted (it's neither important, interesting, nor easily understood). In contrast, ES for example doesn't specify how OOP works at all… – Bergi Mar 30 '16 at 18:43
  • Well... this is getting a bit surreal, but I have to say I very strongly disagree with your assertion that it's not important, *and* your assertion that it's not interesting... *and*, most of all, your assertion that it's not easily understood: it would be crystal clear if they would use their defined term "isCallable" when that's what they're talking about... *if* that's really what they mean when they define what typeof == "function" should do, as you claim. If your claim is true, this part of the spec is sure looking like a boatload of words thrown around with utter failure to communicate. – Don Hatch Mar 30 '16 at 18:56