Skip to content
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

Pull request created for 1.0 vs. 1.1 diffs. #85

Closed
wants to merge 68 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
65e0487
Work around jekyll rendering bug. See #56.
briancavalier Dec 7, 2012
46f7399
Minor updates to promise states wording
domenic Jan 27, 2013
b6ba05a
Fix #74. Clarify that all handlers must execute
briancavalier Feb 12, 2013
223b599
Specify the `this` value must not be special. Closes #58.
domenic Feb 9, 2013
a75cbbb
Terminology clarification on "thenable" and "exception."
domenic Feb 9, 2013
768bee5
Add a conformant implementations list.
domenic Feb 9, 2013
90833e9
Move "must not change" definition into relevant section.
domenic Feb 16, 2013
9b8722c
Use relative links, now that GitHub supports it.
domenic Feb 21, 2013
9697618
Use fenced code blocks, now that GitHub pages support them.
domenic Feb 21, 2013
0e23223
`undefined` is code, mark it as such.
domenic Feb 21, 2013
7fdf92a
Added Naive Promesse
val1984 Mar 5, 2013
0ef0acd
Add promise-as3 and P implementations.
domenic Mar 7, 2013
853f08f
Add the "Thenable Assimilation Procedure". Closes #75.
domenic Feb 17, 2013
ad57184
Be less specific about calling `thenable.then`.
domenic Mar 11, 2013
49f5f2f
Add changelog for 1.0 to 1.1.
domenic Feb 21, 2013
0cb1cbc
Added node-fate to Promises/A+ implementations
shanewholloway Mar 11, 2013
78fb508
Updates based on review comments.
domenic Mar 15, 2013
2a55eb7
Add when 2.0.0 to implementations
briancavalier Mar 19, 2013
20fb88d
Merge pull request #90 from briancavalier/add-when-200
briancavalier Mar 20, 2013
f180200
Add Covenant to implementations.
domenic Mar 21, 2013
d08e9dd
Replace thenable assimilation with resolution.
domenic Mar 15, 2013
b468e50
Handle exceptions thrown by getting `x.then`.
domenic Mar 15, 2013
be4524d
Spell the resolution procedure as `[[Resolve]]`.
domenic Apr 4, 2013
40168f9
Fix typo.
domenic Apr 4, 2013
0cfdf50
Add Pinky to implementations list.
domenic Apr 4, 2013
ca07ea2
added ondras/promise
ondras Apr 11, 2013
983582e
Add ff to implementations list.
domenic Apr 12, 2013
df73815
Adding D.js to implementations list.
malko Apr 14, 2013
65eb7d4
Update Covenant's description.
domenic Apr 20, 2013
1e3b123
Consistently use "reason" rather than "rejection reason". See #80
briancavalier Apr 23, 2013
ac41301
Similarly, use "value" instead of "fulfillment value". thanks @domenic
briancavalier Apr 23, 2013
ae48d4a
Add Shvua to list of implementations.
arieh Apr 22, 2013
ee27726
Handle thenables that fulfill with themselves.
domenic Apr 20, 2013
def6e2d
Allow vicious cycle detection.
domenic Apr 20, 2013
6c24cf2
Mandate `[[Resolve]](p, p)` to throw a `TypeError`.
domenic Apr 26, 2013
4972d6b
Add more implementations!
domenic May 16, 2013
dac2212
Encourage usage of the "promises-aplus" keyword.
domenic May 16, 2013
38ccd40
Add link to FidPromise implementation.
fidian May 16, 2013
417c016
Add Core to list of Promises/A+ implementations
fastner May 16, 2013
9f1d439
Adds nbd.js as a conforming implementation
Aintaer May 26, 2013
e14e654
Add YUI to the list of implementations
juandopazo May 31, 2013
653b7ea
Add Pacta to implementations.
mudge Jun 1, 2013
36d9110
Add @domenic as co-editor
briancavalier Jun 2, 2013
dcfee2f
Merge pull request #123 from briancavalier/add-domenic-as-coeditor
briancavalier Jun 3, 2013
d28d15a
Update YUI link as per @juandopazo's request.
domenic Jun 7, 2013
10d9184
Rewrite the introduction. Closes #106, closes #95.
domenic May 26, 2013
c6786b6
Update differences document for recent changes and clarity.
domenic May 27, 2013
8194fe9
Changelog should reference newer [[Resolve]].
domenic May 27, 2013
556f02a
Add @ForbesLindesay and @erights as contributors.
domenic May 27, 2013
60feafc
Update cujoJS spelling
briancavalier Jun 7, 2013
7567f36
Merge pull request #117 from promises-aplus/new-intro
briancavalier Jun 9, 2013
46791de
Remove self-fulfilling thenable special case.
domenic Jun 8, 2013
7cbacbe
Clarify infinite vs. circular thenable chains.
domenic Jun 8, 2013
1d44146
Update links to use promisesaplus.com.
domenic Jun 8, 2013
97e3162
Create a section for other-language implementations.
domenic Jun 8, 2013
ce2cfa7
Create a section for implementations inside frameworks.
domenic Jun 11, 2013
b56f76c
Merge pull request #126 from promises-aplus/implementations-in-other-…
briancavalier Jun 20, 2013
9ba58eb
Constrain onFulfilled and onRejected to not be called too early.
briancavalier Jun 18, 2013
1721033
Add potch/promise.js implementation.
potch Jul 10, 2013
71a86ae
Add tagline to spec.
briancavalier Jul 9, 2013
e5c029f
Fix missing space.
domenic Aug 3, 2013
1a4cb7d
Update and improve changelog.
domenic Aug 3, 2013
15410d4
Fix list item spacing for Promise States section.
domenic Aug 3, 2013
f35ef37
Enforce a clean stack for `onFulfilled` and `onRejected`.
domenic Aug 14, 2013
ffe71f6
Add SHXPromise to implementations
MSNexploder Aug 26, 2013
cb472c7
Merge pull request #142 from MSNexploder/patch-1
briancavalier Aug 29, 2013
c1f3082
add bluebird to implementations
petkaantonov Sep 27, 2013
e5fbe41
Fix #146: note references should be 3.x, not 4.x.
domenic Sep 29, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 63 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,118 @@
# Promises/A+

This proposal clarifies the behavioral clauses of the [Promises/A proposal](http://wiki.commonjs.org/wiki/Promises/A), extending it to cover *de facto* behaviors and omitting parts that are underspecified or problematic.
**An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.**

As with Promises/A, this proposal does not deal with how to create, fulfill, or reject promises.
A *promise* represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled.

For a full description of the differences between Promises/A+ and Promises/A, see [Differences from Promises/A](promises-spec/blob/master/differences-from-promises-a.md).
This specification details the behavior of the `then` method, providing an interoperable base which all Promises/A+ conformant promise implementations can be depended on to provide. As such, the specification should be considered very stable. Although the Promises/A+ organization may occasionally revise this specification with minor backward-compatible changes to address newly-discovered corner cases, we will integrate large or backward-incompatible only after careful consideration, discussion, and testing.

## General
Historically, Promises/A+ clarifies the behavioral clauses of the earlier [Promises/A proposal](http://wiki.commonjs.org/wiki/Promises/A), extending it to cover *de facto* behaviors and omitting parts that are underspecified or problematic.

A promise represents a value that may not be available yet. The primary method for interacting with a promise is its `then` method.
Finally, the core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, choosing instead to focus on providing an interoperable `then` method. Future work in companion specifications may touch on these subjects.

## Terminology

1. "promise" is an object or function that defines a `then` method.
1. "value" is any legal JavaScript value (including `undefined` or a promise).
1. "promise" is an object or function with a `then` method whose behavior conforms to this specification.
1. "thenable" is an object or function that defines a `then` method.
1. "value" is any legal JavaScript value (including `undefined`, a thenable, or a promise).
1. "exception" is a value that is thrown using the `throw` statement.
1. "reason" is a value that indicates why a promise was rejected.
1. "must not change" means immutable identity (i.e. `===`), but does not imply deep immutability.

## Requirements

### Promise States

A promise must be in one of three states: pending, fulfilled, or rejected.

1. When in pending, a promise:
1. When pending, a promise:
1. may transition to either the fulfilled or rejected state.
1. When in fulfilled, a promise:
1. When fulfilled, a promise:
1. must not transition to any other state.
1. must have a value, which must not change.
1. When in rejected, a promise:
1. When rejected, a promise:
1. must not transition to any other state.
1. must have a reason, which must not change.

Here, "must not change" means immutable identity (i.e. `===`), but does not imply deep immutability.

### The `then` Method

A promise must provide a `then` method to access its current or eventual fulfillment value or rejection reason.
A promise must provide a `then` method to access its current or eventual value or reason.

A promise's `then` method accepts two arguments:

```
```js
promise.then(onFulfilled, onRejected)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is then a free function or can it rely on being called on promise? That is, are the following invocations equivalent? promise.then() versus (promise.then)().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's implementation dependent. We call it a "method," which generally implies it is attached to promise, although it doesn't necessarily have to be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may lead to subtle bugs when changing between implementations. Though I suspect the lowest common denominator is it being attached to promise, and whether it's a free function doesn't take away from that.

Applying then to a different promise object is undefined, so we shouldn't have to worry about edge cases there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point @novemberborn, but I'm also not too concerned about specifying. I think people will tend to call it like promise.then(...) since the name "then" lends itself to being very readable that way, and there are (obviously) strong use cases for passing around the promise, but the use cases for simply passing around a then method aren't as clear (even though that'd work just fine with some implementations)

We could clarify with a note suggesting that then be called with it's associated promise as the thisArg, but I don't feel strongly that we need to.

```

1. Both `onFulfilled` and `onRejected` are optional arguments:
1. If `onFulfilled` is not a function, it must be ignored.
1. If `onRejected` is not a function, it must be ignored.
1. If `onFulfilled` is a function:
1. it must be called after `promise` is fulfilled, with `promise`'s fulfillment value as its first argument.
1. it must be called after `promise` is fulfilled, with `promise`'s value as its first argument.
1. it must not be called before `promise` is fulfilled.
1. it must not be called more than once.
1. it must not be called if `onRejected` has been called.
1. If `onRejected` is a function,
1. it must be called after `promise` is rejected, with `promise`'s rejection reason as its first argument.
1. it must be called after `promise` is rejected, with `promise`'s reason as its first argument.
1. it must not be called before `promise` is rejected.
1. it must not be called more than once.
1. it must not be called if `onFulfilled` has been called.
1. `then` must return before `onFulfilled` or `onRejected` is called [[4.1](#notes)].
1. `onFulfilled` or `onRejected` must not be called until the [execution context](http://es5.github.io/#x10.3) stack contains only platform code. [[3.1](#notes)].
1. `onFulfilled` and `onRejected` must be called as functions (i.e. with no `this` value). [[3.2](#notes)]
1. `then` may be called multiple times on the same promise.
1. If/when `promise` is fulfilled, respective `onFulfilled` callbacks must execute in the order of their originating calls to `then`.
1. If/when `promise` is rejected, respective `onRejected` callbacks must execute in the order of their originating calls to `then`.
1. `then` must return a promise [[4.2](#notes)].
1. If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the order of their originating calls to `then`.
1. If/when `promise` is rejected, all respective `onRejected` callbacks must execute in the order of their originating calls to `then`.
1. `then` must return a promise [[3.3](#notes)].

```
```js
promise2 = promise1.then(onFulfilled, onRejected);
```

1. If either `onFulfilled` or `onRejected` returns a value that is not a promise, `promise2` must be fulfilled with that value.
1. If either `onFulfilled` or `onRejected` throws an exception, `promise2` must be rejected with the thrown exception as the reason.
1. If either `onFulfilled` or `onRejected` returns a promise (call it `returnedPromise`), `promise2` must assume the state of `returnedPromise` [[4.3](#notes)]:
1. If `returnedPromise` is pending, `promise2` must remain pending until `returnedPromise` is fulfilled or rejected.
1. If/when `returnedPromise` is fulfilled, `promise2` must be fulfilled with the same value.
1. If/when `returnedPromise` is rejected, `promise2` must be rejected with the same reason.
1. If either `onFulfilled` or `onRejected` returns a value `x`, run the Promise Resolution Procedure `[[Resolve]](promise2, x)`.
1. If either `onFulfilled` or `onRejected` throws an exception `e`, `promise2` must be rejected with `e` as the reason.
1. If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled with the same value.
1. If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected with the same reason.

### The Promise Resolution Procedure

The **promise resolution procedure** is an abstract operation taking as input a promise and a value, which we denote as `[[Resolve]](promise, x)`. If `x` is a thenable, it attempts to make `promise` adopt the state of `x`, under the assumption that `x` behaves at least somewhat like a promise. Otherwise, it fulfills `promise` with the value `x`.

This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant `then` method. It also allows Promises/A+ implementations to "assimilate" nonconformant implementations with reasonable `then` methods.

To run `[[Resolve]](promise, x)`, perform the following steps:

1. If `promise` and `x` refer to the same object, reject `promise` with a `TypeError` as the reason.
1. If `x` is a promise, adopt its state [[3.4](#notes)]:
1. If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.
1. If/when `x` is fulfilled, fulfill `promise` with the same value.
1. If/when `x` is rejected, reject `promise` with the same reason.
1. Otherwise, if `x` is an object or function,
1. Let `then` be `x.then`. [[3.5](#notes)]
1. If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with `e` as the reason.
1. If `then` is a function, call it with `x` as `this`, first argument `resolvePromise`, and second argument `rejectPromise`, where:
1. If/when `resolvePromise` is called with a value `y`, run `[[Resolve]](promise, y)`.
1. If/when `rejectPromise` is called with a reason `r`, reject `promise` with `r`.
1. If both `resolvePromise` and `rejectPromise` are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
1. If calling `then` throws an exception `e`,
1. If `resolvePromise` or `rejectPromise` have been called, ignore it.
1. Otherwise, reject `promise` with `e` as the reason.
1. If `then` is not a function, fulfill `promise` with `x`.
1. If `x` is not an object or function, fulfill `promise` with `x`.

If a promise is resolved with a thenable that participates in a circular thenable chain, such that the recursive nature of `[[Resolve]](promise, thenable)` eventually causes `[[Resolve]](promise, thenable)` to be called again, following the above algorithm will lead to infinite recursion. Implementations are encouraged, but not required, to detect such recursion and reject `promise` with an informative `TypeError` as the reason. [[3.6](#notes)]

## Notes

1. In practical terms, an implementation must use a mechanism such as `setTimeout`, `setImmediate`, or `process.nextTick` to ensure that `onFulfilled` and `onRejected` are not invoked in the same turn of the event loop as the call to `then` to which they are passed.
1. Here "platform code" means engine, environment, and promise implementation code. In practice, this requirement ensures that `onFulfilled` and `onRejected` execute asynchronously, after the event loop turn in which `then` is called, and with a fresh stack. This can be implemented with either a "macro-task" mechanism such as [`setTimeout`](http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers) or [`setImmediate`](https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html#processingmodel), or with a "micro-task" mechanism such as [`MutationObserver`](http://dom.spec.whatwg.org/#interface-mutationobserver) or [`process.nextTick`](http://nodejs.org/api/process.html#process_process_nexttick_callback). Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or "trampoline" in which the handlers are called.

1. That is, in strict mode `this` will be `undefined` inside of them; in sloppy mode, it will be the global object.

1. Implementations may allow `promise2 === promise1`, provided the implementation meets all requirements. Each implementation should document whether it can produce `promise2 === promise1` and under what conditions.

1. The mechanism by which `promise2` assumes the state of `returnedPromise` is not specified. One reasonable approach is to call `returnedPromise.then(fulfillPromise2, rejectPromise2)`, where:
1. `fulfillPromise2` is a function which fulfills `promise2` with its first parameter.
1. `rejectPromise2` is a function which rejects `promise2` with its first parameter.
1. 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.

1. This procedure of first storing a reference to `x.then`, then testing that reference, and then calling that reference, avoids multiple accesses to the `x.then` property. Such precautions are important for ensuring consistency in the face of an accessor property, whose value could change between retrievals.

Given that `returnedPromise` may not be Promises/A+-compliant, but could instead be any object with a `then` method, it isn't always possible to satisfy the requirement of `promise2` assuming the same state as `returnedPromise`. Thus, the procedure here represents a best-faith effort.
1. Implementations should *not* set arbitrary limits on the depth of thenable chains, and assume that beyond that arbitrary limit the recursion will be infinite. Only true cycles should lead to a `TypeError`; if an infinite chain of distinct thenables is encountered, recursing forever is the correct behavior.

---

Expand Down
32 changes: 32 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Promises/A+ Changelog

## [Version 1.1][] / 2013-??-??

### Informative Changes

These changes help clarify and improve the specification, but should not affect implementation conformance.

- Improved the introductory text to give a better idea of what promises are, and what the purpose of Promises/A+ is.
- Separated out the concept of a possibly-nonconformant "thenable" from the concept of a conformant "promise."
- Added a definition for "exception."

### Normative Changes

These changes impose new requirements on implementations, either to specify a previously-undefined behavior, or to fix something incorrect that the spec allowed in version 1.0.

- Specified that `onFulfilled` and `onRejected` must be called as functions, with no `this` value.
- Changed the way in which asynchronicity was mandated for `onFulfilled` and `onRejected`, to enforce the important invariant that the stack be clear. In particular, the new wording prevents fulfilling or rejecting a promise from ever synchronously calling the handlers.
- Prohibited implementations from calling `onFulfilled` or `onRejected` before the corresponding promise was respectively fulfilled or rejected.
- Specified the Promise Resolution Procedure, instead of leaving the mechanism for adopting a thenable's state unspecified.
- The recursive nature of the now-specified procedure improves upon the naïve non-recursive suggestion given in version 1.0.
- Edge cases, such as how to deal with a getter for `then`, or synchronous exceptions that could be thrown during the process, are nailed down.
- Self-resolution now causes rejection with a `TypeError`.
- Infinite recursion from circular thenable chains are discussed, optionally allowing this to result in a `TypeError`.

## [Version 1.0][] / 2012-12-06

- Initial release. For differences from its predecessor, the Promises/A specification, see [Differences from Promises/A](differences-from-promises-a.md).


[Version 1.0]: https://github.com/promises-aplus/promises-spec/tree/1.0.0
[Version 1.1]: https://github.com/promises-aplus/promises-spec/tree/1.1.0
7 changes: 5 additions & 2 deletions credits.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ Promises/A+ draws heavily from the following works, and the majority of credit g
* [Promises/A proposal](http://wiki.commonjs.org/wiki/Promises/A) by Kris Zyp ([@kriszyp](https://github.com/kriszyp)), and
* [UncommonJS Thenable Promises](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md) specification by Kris Kowal ([@kriskowal](https://github.com/kriskowal))

## Editor
## Editors

The primary editor of the Promises/A+ specification is Brian Cavalier ([@briancavalier](https://github.com/briancavalier)).
* Brian Cavalier ([@briancavalier](https://github.com/briancavalier))
* Domenic Denicola ([@domenic](https://github.com/domenic))

## Contributors

Expand All @@ -19,6 +20,8 @@ The following people assembled and contributed to the concepts and content in Pr
* Domenic Denicola ([@domenic](https://github.com/domenic))
* Yehuda Katz ([@wycats](https://github.com/wycats))
* Kris Kowal ([@kriskowal](https://github.com/kriskowal))
* Forbes Lindesay ([@ForbesLindesay](https://github.com/ForbesLindesay))
* Mark Miller ([@erights](https://github.com/erights))
* Luke Smith ([@lsmith](https://github.com/lsmith))

## Logo
Expand Down
17 changes: 11 additions & 6 deletions differences-from-promises-a.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ Promises/A+ is based on the concepts and `then` API presented in the [CommonJS P
The following parts of Promises/A have been intentionally omitted:

1. Progress handling: in practice, it has proven to be underspecified and currently does not have an agreed-upon or *de facto* behavior within the promise implementor community.
1. Interactive promise: this is deemed out of scope of the minimal API necessary for interoperable promises.
1. Interactive promises: this is deemed out of scope of the minimal API necessary for interoperable promises.
1. `promise1 !== promise2` is *not* a requirement for `var promise2 = promise1.then(onFulfilled, onRejected)`.

## Clarifications

1. Promise terminology: specifically, Promises/A+ standardizes on the terms "fulfilled", "rejected", "reason", and "pending".
Promises/A+ uses different terminology from Promises/A, reflecting what has become the *de facto* vocabulary among promise implementations. Specifically:

1. The promise states are given as "pending", "fulfilled", and "rejected".
1. When promises are fulfilled, they have a "value"; when they are rejected, they have a "reason".
1. It introduces the term "thenable" as distinct from "promise", so as to more precisely talk about the duck-typing tests necessary for implementation interoperation.

## Additions

Promises/A+ additionally specifies:

1. the behavior in the case where `onFulfilled` or `onRejected` returns a promise;
1. the rejection reason passed to `onRejected` must be the thrown exception in the case where a handler throws;
1. the behavior in the case where `onFulfilled` or `onRejected` returns a thenable, including the details of the resolution procedure;
1. the reason passed to `onRejected` must be the thrown exception in the case where a handler throws;
1. `onFulfilled` and `onRejected` must be called asynchronously;
1. strict ordering of calls to `onFulfilled` and `onRejected` for subsequent calls to `then` on the same promise;
1. `promise1 !== promise2` is *not* a requirement for `var promise2 = promise1.then(onFulfilled, onRejected)`.
1. `onFulfilled` and `onRejected` must be called as functions;
1. strict ordering of calls to `onFulfilled` and `onRejected` for subsequent calls to `then` on the same promise.
Loading