Tab Atkins Jr.
jackalmage at gmail.com
Tue Aug 20 11:04:08 PDT 2013
On Tue, Aug 20, 2013 at 9:18 AM, Mark S. Miller <erights at google.com> wrote:
> To answer this precisely, we need good terminology to distinguish two levels
> of abstraction: The distinctions observable to the AP2.flatMap programmer
> and the coarser distinctions observable to the AP2.then programmer. Let's
> start ignoring thenables and considering only promises-vs-non-promises.
> Let's also start by ignoring rejection.
> At the AP2.flatMap level,
> * for a promise p and an arbitrary value v, p may accept v. p is then in
> the "accepted" state.
> * for a promise p and a promise q, p may adopt q. p is then in the
> "adopting" state.
> Putting these together, we can also say
> * for a promise p and an arbitrary value v, p is resolved to v if p either
> accepts v or adopts v. p is then in the "resolved" state.
> p2 = p1.flatMap(v1 => q2)
> means, if p1 is accepted, then v1 will be what it has accepted.
> If q2 is a promise, then p2 adopts q2.
> p2.flatMap(...) fires as a result of acceptance but not adoption. If q2
> accepts, then p2 likewise accepts and p2.flatMap fires by virtue of this
> At the P2.then level
> * for a promise p and a non-promise v, p may be fulfilled with v. p is
> then in the fulfulled state.
> * for a promise p and a promise q, p may follow q. p is then in the
> following state.
> * Until a promise p is either fulfilled or rejected, it is pending.
> Putting these together, we can also say
> * for a promise p and an arbitrary value v, p is resolved to v if either p
> is fulfilled with v or p follows v. p is then in the "resolved" state.
> p4 = p3.then(v3 => v4)
> means, if p3 is fulfilled, then v3 will be what p3 is fulfilled with.
> p4 is resolved to v4. If v4 is a promise, then p4 follows v4. Else p4 is
> fulfilled with v4.
> p4.then fires as a result of fulfillment but not following. If p4 follows v4
> and v4 fulfills, then p4 likewise fulfills and p4.then fires by virtue of
> this fulfillment.
> Notice that "resolved" is the same states at each level, even though these
> states are described differently. That is why we can use the same term at
> both levels. Likewise, the concept of "unresolved" is meaningful at both
Argh, I knew this would turn into another confusing terminology discussion. ^_^
I'm not quite getting this. Why are you using "resolved" in this way?
It doesn't seem to map to a useful state for either mode, since
you're munging together the case where v4 is a value (p4 can call its
callbacks) and where v4 is a promise (p4 maybe can't call its
callbacks yet, or ever, depending on v4's state). You're also munging
together the case where q2 is pending vs not-pending, which again
means that either p2 can call its callbacks or not.
In my email, and I think Domenic in his, I'm trying to nail down some
terms that map to useful states, capturing observable distinctions in
"resolved" means a promise contains a value - it's no longer pending.
Your p2 is resolved only when q2 becomes resolved, due to adoption
semantics. (If you were to put q2 directly into another promise, via
`p2 = Promise.resolve(q2)`, then p2 would be resolved. Adoption
semantics flatten one level, but `Promise.resolve()` isn't adopting.)
A promise is "resolved" when it would call its flatMap() callbacks.
"fulfilled", taken from Promises/A+, means a promise contains a
non-promise value, or contains a fulfilled promise. Your p4 is only
fulfilled if v4 is a non-promise value, or is a fulfilled promise.
So, a promise starts out "pending", becomes "resolved", and then
becomes "fulfilled". This ordering is always preserved, though some
states might happen at the same time.
If necessary, we can come up with distinct terms for "not resolved"
and "not fulfilled", since a promise can be resolved but not
fulfilled. (This is exactly the state that p4 is in if v4 is a
non-fulfilled promise.) "Not fulfilled" = "pending" (Promises/A+
meaning) and "not resolved" = "super pending"? ^_^
We could use the terms differently than what I've defined here, but why?
> I'm not so much concerned with the static .resolve method, since the extra
> storage cost for the static method is negligible. However, what does
> aResolve.resolve do? If it causes its promise to accept, this must be
> observably different to .flatMap observers than if it causes its promise to
> adopt. This difference is not observable to .then observers, which is why
> I've accidentally missed this issue twice now. But since an implementation
> cannot know ahead of time whether there might be .flatMap observers, using
> .accept for .resolve would impose prohibitive storage costs on .then
> oriented patterns. See the message Anne linked to.
I'm not entirely certain I get your point, so let me restate in code
and hopefully clearer text. Given this code:
p1 = Promise.resolve(v1)
p2 = Promise.resolve(p1)
p2.flatMap(print) would print the p1 object, but p2.then(print) would
print v1. If p2 was the only thing referring to p1, and we somehow
knew that you'd only interact with p2 via .then(), we could GC p1 and
just keep v1 around. However, since we don't know that, we have to
keep both p1 and v1 around, which is an extra memory cost.
Is this what you were referring to?
(I have no idea why this paragraph I'm responding to draws a
distinction between Promise.resolve() and the
PromiseResolver#resolve() method, though. `Promise.resolve(v1)` is
exactly identical to `new Promise(r=>r.resolve(v1))` - it's just a
>> (Note that if anyone thinks we need something that eagerly flattens a
>> promise, rather than flattening happening implicitly via the
>> definition of "fulfilled value" for then(), realize that this eager
>> flattening operation is hostile to lazy promises. While this might be
>> *useful* in some cases, it's probably not something we need or want in
>> the core language, or if we do, it should be given an appropriately
>> descriptive name, rather than yet another synonym for "accept".)
> I agree that we do not need eager flattening.
I don't understand what you're asking for, then.
More information about the es-discuss