This proposal attempts to clarify the behavioral clauses of the Promises/A proposal, to extend it to cover de facto behaviors, and to omit parts that have proven to be underspecified or problematic.
As with Promises/A, this proposal does not deal with creation of promises, nor the mechanisms for fulfilling or rejecting them.
For a full description of the differences between Promises/A+ and Promises/A, see Promises/A+ Differences from Promises/A.
- "value" is any legal language value, including
undefined
, that is not a promise. - "reason" is a value. The term "reason" is used here because it is used in existing promise literature, and helps to reinforce the difference between fulfilled and rejected promise states. It also conveys the intent that a reason should represent the "reason the associated promise has been rejected."
- "must not change" means immutable identity (e.g.
===
), and does not imply deep immutability.
A promise represents a value that may not be available yet. A promise must be one of three states: pending, fulfilled, or rejected:
- In the pending state, a promise
- must provide a way to arrange for a function to be called with its fulfillment value after it has transitioned to the fulfilled state, or with its reason for being rejected after it has transitioned to the rejected state.
- may transition to either the fulfilled or rejected state.
- In the fulfilled state, a promise
- must have a value, which must not change.
- must provide a way to arrange for a function to be called with that value.
- must not transition to any other state.
- In the rejected state, a promise
- must have a reason, which must not change.
- must provide a way to arrange for a function to be called with that reason.
- must not transition to any other state.
A promise is an object or function that defines a then
method that accepts the following two arguments:
promise.then(onFulfilled, onRejected)
-
Both
onFulfilled
andonRejected
are optional arguments. -
If
onFulfilled
is a function,- it must be called after
promise
is fulfilled, withpromise
's value as its first argument. - it must not be called more than once.
- it must not be called if
onRejected
has already been called.
- it must be called after
-
If
onFulfilled
is not a function, it must be ignored. -
If
onRejected
is a function,- it must be called after
promise
is rejected, withpromise
's reason as its first argument. - it must not be called more than once.
- it must not be called if
onFulfilled
has already been called.
- it must be called after
-
If
onRejected
is not a function, it must be ignored. -
onFulfilled
andonRejected
must not be called beforethen
returns [1]. -
onFulfilled
andonRejected
supplied in one call tothen
must never be called after those supplied to a later call tothen
on the same promise. -
then
may be called any number of times. -
then
must return a promise [2]var promise2 = promise1.then(onFulfilled, onRejected)
- If
onFulfilled
is not a function andpromise1
is fufilled,promise2
must be fulfilled with the same fulfillment value. - If
onRejected
is not a function andpromise1
is rejected,promise2
must be rejected with the same reason. - If either
onFulfilled
oronRejected
returns a value,promise2
must be fulfilled with that value. - If either
onFulfilled
oronRejected
throws an exception,promise2
must be rejected with the thrown exception as the reason. - If either
onFulfilled
oronRejected
returns a promise (call itreturnedPromise
),promise2
must be placed into the same state asreturnedPromise
:- If
returnedPromise
is fulfilled,promise2
must be fulfilled with the same value. - If
returnedPromise
is rejected,promise2
must be rejected with the same reason. - If
returnedPromise
is pending,promise2
must also be pending. WhenreturnedPromise
is fulfilled,promise2
must be fulfilled with the same value. WhenreturnedPromise
is rejected,promise2
must be rejected with the same reason.
- If
- If
-
In practical terms, an implementation must use a mechanism such as
setTimeout
, or a faster alternative, where available, such assetImmediate
orprocess.nextTick
, to ensure thatonFulfilled
oronRejected
are not invoked in the same turn of the event loop as the call tothen
to which they are passed. -
Each implementation should document whether it may produce
promise2
===promise1
, and if so, under what conditions. It is intentionally not specified as to whether the returned promise may be the same promise, or must be a new promise, i.e.promise2
!==promise1
is not a requirement. An implemention is free to allowpromise2
===promise1
, provided it can meet the requirements in this section.