-
Notifications
You must be signed in to change notification settings - Fork 165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New wording for thenable resolution #124
Conversation
The v1.1 language for the Promise Resolution Procedure dictates steps for an implementation rather than the rules that an implementation must follow. The rest of the spec is in the form of a set of rules, leaving the implementation details up to the implementer. Whatever sausage is under the hood, as long as the test suite is past, it is a conformant implementation. This attempts to normalize the language into the set-of-rules style.
👍 I like specs with algorithms, but this makes more sense for A+ and the community. |
I think one of the most interesting things about Promises/A+ is that the spec encourages innovation since we haven't locked everything down to the exact algorithm level. For that reason, I'm glad to see @lsmith take a shot at this. There are some tricky bits in this algorithm, though, so I'm def interested to give it a thorough read. Should have time over the next day or two. |
Left a few little comments, but I want to read the two approaches together to make sure they do cover the same cases. They are so different that it's tough to compare :) |
1. If either callback returns a value which is an object or function with a `then` method, but the value cannot be identified as a promise (call the value `thenable`) attempt to fulfill or reject `promise2` via `thenable`'s `then` method[[4.6](#notes)]. | ||
|
||
```js | ||
thenable.then(resolver, rejecter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these names are a bit confusing, especially resolver
. I'd prefer the more verbose resolvePromise2
and rejectPromise2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From DOM Promises: resolveCallback
and rejectCallback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like resolvePromise2
and rejectPromise2
because they are both explicit and contrived. It would be difficult for someone reading the spec to think that the names might have a more general connotation, or might be found elsewhere in the spec.
- Rename
resolver
andrejecter
toresolvePromise2
andrejectPromise2
I am hesitantly in favor of this. There are two major weaknesses I currently see:
|
I also think that the current style of stating requirements for |
@domenic could you explain your second bullet point ("harder to refactor ... with a ... promise constructor")? That sounds important, but I didn't understand what you're getting at. |
|
||
Due to the recursive nature of this procedure, it is possible for a conformant implementation to cause infinite recursion if a promise is resolved with a thenable that participates in a circular thenable chain. Implementations are allowed, but not required, to detect such occurrences and instead reject `promise` with an informative `TypeError` as the reason. | ||
1. If either callback (`onFulfilled` or `onRejected`) throws an exception, `promise2` must be rejected with the thrown exception as the reason. | ||
1. If either callback returns a value that is not an object or function with a `then` method[[4.5](#notes)], `promise2` must be fulfilled with that value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the way to arrange this might be:
- if they throw, reject
- if they return
promise2
, reject withTypeError
- if they return a promise, adopt state
- otherwise, let
then
bereturnValue.then
,- if this step throws, reject with thrown error
- if
then
is a function, call it, etc. - otherwise, fulfill with
returnValue
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had almost that exact arrangement in a working draft, but went back from "otherwise" to "if" because it would be the only use of the word in the spec and, for me at least, began mirroring if/else
blocks which suggested code structure. Also, the nesting started to get a little deep (the irony). The bullets are exclusive of one another, so "otherwise" does makes sense. Other opinions?
The point about accessing then
only once being in the spec vs in the notes I'm on the fence about. Like thenable.then(resolvePromise2, rejectPromise2)
it is an implementation detail, not a rule, technically. The justification for it is good, albeit edge-casey. I'm willing to go with whichever placement is deemed best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think "otherwise" works well in this case, and I like that @domenic's ordering groups the two "outer" error conditions together as 1 and 2**. I'm also on the fence about explicitly stating that accessing thenable.then
once is a requirement. However, it seems possible to write the steps in such a way that, when followed closely, you'd end up only accessing it only once anyway.
EDIT: **See my other note about step 2 not being the error condition we care about, tho :)
@briancavalier I meant that the promise constructor spec linked is currently very nice and simple: it states:
But if we eliminated |
@domenic "A |
@domenic re: Promise constructor referencing [[Resolve]]. Got it, thanks for clarifying. Yeah, have to think about how to handle that. |
I am no longer really in favor of this. It hasn't kept up with recent changes, and has unaddressed concerns. I like the precision of our current resolution procedure; it seems like exactly the right place to inject that precision, at a very tricky point in the process with lots of edge cases. Plus, uh, I already wrote all the tests for it in that form :P Does anyone else want to champion this? Otherwise, I'd like to close it out, and call 1.1 done ( 💪 !) |
I'm fine closing this. I haven't prioritized keeping up or keeping the PR up with the current state of Promises. It does seem though that the A+ spec has transitioned from an implementation target for JS libs to a reference document/discussion platform for implementation in ES and DOM. If it is serving the role of guidance for soon-to-be JS polyfill implementations, that's fine, and should be accordingly specific. If my assessment isn't off base, I consider that change a very good thing. However, if that is the case, do you think it would be advantageous to push a 1.1 when the state of native promises is (seemingly) close to resolution? Or is there already accepted agreement in ES and DOM for the behavior of |
Hmm, well, to some extent I think this is true. But Promises/A+ is also still focused, IMO primarily, on its original mission, which is interoperable promise implementation standards as driven by implementers. The DOM folks and TC39 are suffering from some non-implementer consensus-blockers that bloat their standards and drive them in unfortunate directions. Any user-space implementations by us, the people who have been doing this for a while, are unlikely to adopt such unfortunate antifeatures. So there's still a place for a community-driven standard. We also have different restrictions than they do, e.g. inter-library compatibility (see assimilation of thenables) or legacy compatibility (see ignore-vs-reject for non- (All of the above is my opinion, to be clear, and not necessarily reflective of everyone in Promises/A+-land.)
This is a kind of funny sentence, because the difference between polyfill implementations and just plain user-space promise implementations seems to come down to which standard (DOM/TC39 or Promises/A+) you are implementing. Heh :)
I definitely think it would be. Native promises are still in some flux, and even if they do settle down, it might not be in an exemplary direction. The DOM spec has notable bugs and divergences, and TC39 promises are currently a Lovecraftian mismash of monads and promises into the same object. Providing a clear example of where the community wants to go and what it considers important, in the form of Promises/A+ 1.1, seems like a worth goal. We still have time to influence these things substantially before they start shipping, without flags, as part of public APIs, in more than one major browser. Even if we don't influence them, I still believe promise libraries will have a place in the native-promise future, whether it be augmenting native promises with lots of methods or prototyping cool new features like unhandled rejection tracking, cancellation, synchronous state inspection, or generator integration. So to reiterate, I think continuing as we are is good. I and others will of course continue to do outreach to the more official standards bodies, but having Promises/A+ 1.1 as a product of our community-driven open spec development process is worthwhile, both for that outreach and inherently toward our original goals. |
The v1.1 language for the Promise Resolution Procedure dictates steps for an
implementation rather than the rules that an implementation must follow. The
rest of the spec is in the form of a set of rules, leaving the implementation
details up to the implementer. Whatever sausage is under the hood, as long as
the test suite is passed, it is a conformant implementation.
This attempts to normalize the language into the set-of-rules style.