This proposal attempts to clarify the behavioral clauses of the Promises/A proposal, and to extend it to cover the cases where handlers may return a promise.
This proposal intentionally omits the progress handling portion of Promises/A. In practice it has proven to be underspecified and currently does not have an agreed-upon or defacto behavior within the promise implementor community.
Also intentionally omitted is a requirement for calling fulfill and broken handlers either synchronously or asynchronously [1]. Promises/A itself does not specify, and both synchronous and asynchronous approaches exist in the current landscape of popular implementations.
This specification borrows heavily from the Promises/A proposal by Kris Zyp, as well as the UncommonJS Thenable Promises specification by Kris Kowal. All credit goes to those authors.
As with Promises/A, this proposal does not deal with creation of promises.
A promise represents a value that may not be available yet. A promise must be one of three states: pending, fulfilled, or broken:
-
When in the pending state, a promise may transition to either the fulfilled or broken state.
-
When in the fulfilled state, a promise has a value and provides a way to arrange for a function to be called with that value. Once a promise has transitioned to the fulfilled state, it must never transition to any other state.
-
When in the broken state, a promise has a reason (an indication of why it was broken) and provides a way to arrange for a function to be called with that reason. Once a promise has transitioned to the broken state, it must never transition to any other state.
A promise is an object or function that defines a then
method that accepts at least 2 arguments:
promise.then(fulfilled, broken)
-
Both
fulfilled
andbroken
are optional arguments -
If truthy,
fulfilled
must be a function that accepts a value as its first argument.- When
promise
is fulfilled,fulfilled
will be called withpromise
's fulfillment value. fulfilled
will never be called more than once.fulfilled
will never be called ifbroken
has already been called.
- When
-
If truthy,
broken
must be a function that accepts a reason (which must be a value, not a promise) as its first argument.- When
promise
is broken,broken
will be called withpromise
's reason for being broken. broken
will never be called more than once.broken
will never be called iffulfilled
has already been called.
- When
-
then
may be called any number of times. -
fulfilled
andbroken
supplied in one call tothen
must never be called after those supplied to a later call tothen
on the same promise. -
then
must return a promise [2]var promise2 = promise1.then(fulfilled, broken)
- When
promise1
is either fulfilled andfulfilled
is called with the fulfillment value, or broken andbroken
is called with the reason:- If either returns a value,
promise2
must be fulfilled with that value. - If either throws an exception,
promise2
must be broken with the thrown exception as the reason. - If either returns a promise (call it
returnedPromise
),promise2
must be placed into the same state asreturnedPromise
:- If
returnedPromise
is fulfilled,promise2
must be fulfilled with the same fulfillment value. - If
returnedPromise
is broken,promise2
must be broken with the same reason. - If
returnedPromise
is pending,promise2
must also be pending. WhenreturnedPromise
is fulfilled,promise2
must be fulfilled with the same fulfillment value. WhenreturnedPromise
is broken,promise2
must be broken with the same reason.
- If
- If either returns a value,
- When
- Each implementation should document whether it calls handlers synchronously or asynchronously.
- 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.
This looks like @kriskowal's Thenable Promises section almost verbatim. Can you point out the distinctions?
Preamble
resolve()
(or whatever triggering method) is called, either each or all handlers are executed off thread. This allows consistent behavior for executingresolvedOrUnresolvedPromise.then(doStuff)
.handler2
will be invoked afterhandler1
is invoked even ifhandler1
returns a promise. It seems to be the assumed consensus, but I haven't seen it documented anywhere.Promise
resolve()
method of Promise Managers or Deferreds. Naming discussion! But may be important.Requirements
then()
method; no other methods allowed. Am I misunderstanding? This is clean for the purpose of separation, but would likely be ignored in implementation, as it's not conducive to API sugaring. Imagine an ajax related api that generates promises with aparseJSON
method that desugared tothis.then(function (response) { return JSON.parse(response.responseText); })
or an animation related promise factory that generated promises with await(msDelay)
method.fulfilled
orbroken
is not provided. This is especially relevant for middle steps in a promise chain.resolvedPromise.then(what, now)
fulfilled
orbroken
don't return a value.promise2 === promise1
? I can't imagine a use case that would require that behavior. It seems wrong.