What follows is a snapshot https://github.com/domenic/promises-unwrapping/README.md (rendered as HTML) as of 24 October, 2013. We understand this to be the latest draft of a consensus specification for Promises, which we expect to be incorporated into ECMAScript 6 in due course. This copy is made for reference by the JSON-LD API specification, in case the above URL stops working, and to make clear the state of Promises at the time the JSON-LD API specification was completed. We have also archived the rest of that repository (files, zip, tar of clone).
-- Sandro Hawke (sandro@w3.org), on behalf of JSON-LD Task Force of the RDF WG, 24 October 2013
This repository is meant to fully flesh out a subset of the "AP2" promise consensus developed over the last month on es-discuss. In particular, it provides the subset the DOM needs as soon as possible, omitting flatMap
and accept
for now but building a conceptual foundation that would allow them to be added at a later date.
It is meant to succeed the current DOM Promises spec, and fixes a number of bugs in that spec while also changing some of the exposed APIs and behavior to make it more forward-compatible with the full AP2 consensus.
To successfully and consistently assimilate thenable objects into real promises, an implementation must maintain a weak map of thenables to promises. Notably, both the keys and values must be weakly stored. Since this weak map is not directly exposed, it does not need to be a true ECMAScript weak map, with the accompanying prototype and such. However, we refer to it using ECMAScript notation in this spec, i.e.:
ThenableCoercions.has(thenable)
ThenableCoercions.get(thenable)
ThenableCoercions.set(thenable, promise)
The Derived Promise Transform type is used to encapsulate promises which are derived from a given promise, optionally including fulfillment or rejection handlers that will be used to transform the derived promise relative to the originating promise. They are stored in a promise's [[Derived]] internal data property until one of [[HasValue]] or [[HasReason]] becomes true, at which time changes propagate to all derived promise transforms in the list and the list is cleared.
Derived promise transforms are Records composed of three named fields:
The Deffered type is used to encapsulate newly-created promise objects along with functions that resolve or reject them. Deferred objects are derived by the GetDeferred abstract operation from either the Promise constructor itself or from a constructor that subclasses the Promise constructor. This mechanism allows promise subclasses to install custom resolve and reject behavior by creating constructors that pass appropriate functions to their resolver argument.
Deferreds are Records composed of three named fields:
The absract operation GetDeferred takes a potential constructor function, and attempts to use that constructor function in the fashion of the normal promise constructor to extract resolve and reject functions, returning the constructed promise along with those two functions controlling its state. This is useful to support subclassing, as this operation is generic on any constructor that calls a passed resolver argument in the same way as the Promise constructor. We use it to generalize static methods of the Promise constructor to any subclass.
resolver(passedResolve, passedReject)
be an ECMAScript function that lets resolve be passedResolve
and reject be passedReject
.The abstract operation IsPromise checks for the promise brand on an object.
The abstract operation IsResolved checks for whether a promise's fate is resolved.
The abstract operation PropagateToDerived propagates a promise's value or reason to all of its derived promises.
The abstract operation Reject rejects a promise with a reason.
The abstract operation SetReason encapsulates the process of setting a promise's reason and then propagating this to any derived promises.
The abstract operation SetValue encapsulates the process of setting a promise's value and then propagating this to any derived promises.
The abstract operation Then queues up fulfillment and/or rejection handlers on a promise for when it becomes fulfilled or rejected, or schedules them to be called in the next microtask if the promise is already fulfilled or rejected. It returns a derived promise, transformed by the passed handlers.
The abstract operation ToPromise coerces its argument to a promise, ensuring it is of the specified constructor C, or returns the argument if it is already a promise matching that constructor.
The operator Resolve
resolves a promise with a value.
IsResolved(p)
, return.IsPromise(x)
is true
,
SameValue(p, x)
,
selfResolutionError
be a newly-created TypeError
object.SetReason(p, selfResolutionError)
.x.[[Following]]
is not undefined
,
p.[[Following]]
to x.[[Following]]
.{ [[DerivedPromise]]: p, [[OnFulfilled]]: undefined, [[OnRejected]]: undefined }
as the last element of x.[[Following]].[[Derived]]
.x.[[HasValue]]
is true
, call SetValue(p, x.[[Value]])
.x.[[HasReason]]
is true
, call SetReason(p, x.[[Reason]])
.p.[[Following]]
to x
.{ [[DerivedPromise]]: p, [[OnFulfilled]]: undefined, [[OnRejected]]: undefined }
as the last element of x.[[Derived]]
.SetValue(p, x)
.The operator UpdateDerived
propagates a promise's state to a single derived promise using any relevant transforms.
originator.[[HasValue]]
and originator.[[HasReason]]
is true
.originator.[[HasValue]]
is true
,
Type(originator.[[Value]])
is Object
, queue a microtask to run the following:
ThenableCoercions.has(originator.[[Value]])
,
coercedAlready
be ThenableCoercions.get(originator.[[Value]])
.UpdateDerivedFromPromise(derived, coercedAlready)
.thenResult
be Get(originator.[[Value]], "then")
.thenResult
is an abrupt completion, call UpdateDerivedFromReason(derived, thenResult.[[value]])
.IsCallable(thenResult.[[value]])
,
coerced
be CoerceThenable(originator.[[Value]], thenResult.[[value]])
.UpdateDerivedFromPromise(derived, coerced)
.UpdateDerivedFromValue(derived, originator.[[Value]])
.UpdateDerivedFromValue(derived, originator.[[Value]])
.UpdateDerivedFromReason(derived, originator.[[Reason]])
.The operator UpdateDerivedFromValue
propagates a value to a derived promise, using the relevant onFulfilled
transform if it is callable.
IsCallable(derived.[[OnFulfilled]])
, call CallHandler(derived.[[DerivedPromise]], derived.[[OnFulfilled]], value)
.SetValue(derived.[[DerivedPromise]], value)
.The operator UpdateDerivedFromReason
propagates a reason to a derived promise, using the relevant onRejected
transform if it is callable.
IsCallable(derived.[[OnRejected]])
, call CallHandler(derived.[[DerivedPromise]], derived.[[OnRejected]], reason)
.SetReason(derived.[[DerivedPromise]], reason)
.The operator UpdateDerivedFromPromise
propagates one promise's state to the derived promise, using the relevant transform if it is callable.
promise.[[HasValue]]
is true
or promise.[[HasReason]]
is true
, call UpdateDerived(derived, promise)
.derived
as the last element of promise.[[Derived]]
.The operator CallHandler
applies a transformation to a value or reason and uses it to update a derived promise.
result
be handler.[[Call]](undefined, (argument))
.result
is an abrupt completion, call Reject(derivedPromise, result.[[value]])
.Resolve(derivedPromise, result.[[value]])
.The operator CoerceThenable
takes a "thenable" object whose then
method has been extracted and creates a promise from it. It memoizes its results so as to avoid getting inconsistent answers in the face of ill-behaved thenables; the memoized results are later checked by UpdateDerived
.
Type(thenable)
is Object
.IsCallable(then)
.p
be the result of calling PromiseCreate().resolve(x)
be an ECMAScript function that calls Resolve(p, x)
.reject(r)
be an ECMAScript function that calls Reject(p, r)
.result
be then.[[Call]](thenable, (resolve, reject))
.result
is an abrupt completion, call Reject(p, result.[[value]])
.ThenableCoercions.set(thenable, p)
.p
.The Promise constructor is the %Promise% intrinsic object and the initial value of the Promise
property of the global object. When Promise
is called as a function rather than as a constructor, it initiializes its this value with the internal state necessary to support the Promise.prototype
methods.
The Promise
constructor is designed to be subclassable. It may be used as the value of an extends
clause of a class declaration. Subclass constructors that intended to inherit the specified Promise
behavior must include a super
call to the Promise
constructor.
When Promise
is called as part of a new
expression it is a constructor: it initialises a newly created object.
Promise
called as part of a new expression with argument list argumentsList performs the following steps:
Promise
function object on which the new
operator was applied.new
operator.If Promise is implemented as an ordinary function object, its [[Construct]] internal method will perform the above steps.
resolve(x)
be an ECMAScript function that calls Resolve(obj, x)
.reject(r)
be an ECMAScript function that calls Reject(obj, r)
.The abstract operation PromiseCreate is used by the specification to create new promise objects in the pending state.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
all
returns a new promise which is fulfilled with an array of fulfillment values for the passed promises, or rejects with the reason of the first passed promise that rejects. It casts all elements of the passed iterable to promises as it runs this algorithm.
onFulfilled(v)
be an ECMAScript function that:
resolve.[[Call]](undefined, (values))
.Note: The all
function is an intentionally generic utility method; it does not require that its this value be the Promise constructor. Therefore, it can be transferred to or inherited by any other constructors that may be called with a single function argument.
cast
coerces its argument to a promise, or returns the argument if it is already a promise.
Note: The cast
function is an intentionally generic utility method; it does not require that its this value be the Promise constructor. Therefore, it can be transferred to or inherited by any other constructors that may be called with a single function argument.
race
returns a new promise which is settled in the same way as the first passed promise to settle. It casts all elements of the passed iterable to promises as it runs this algorithm.
Note: The race
function is an intentionally generic utility method; it does not require that its this value be the Promise constructor. Therefore, it can be transferred to or inherited by any other constructors that may be called with a single function argument.
reject
returns a new promise rejected with the passed argument.
Note: The reject
function is an intentionally generic factory method; it does not require that its this value be the Promise constructor. Therefore, it can be transferred to or inherited by any other constructors that may be called with a single function argument.
resolve
returns a new promise resolved with the passed argument.
Note: The resolve
function is an intentionally generic factory method; it does not require that its this value be the Promise constructor. Therefore, it can be transferred to or inherited by any other constructors that may be called with a single function argument.
The Promise prototype object is itself an ordinary object. It is not a Promise instance and does not have any of the promise instances' internal data properties, such as [[IsPromise]].
The value of the [[Prototype]] internal data property of the Promise prototype object is the standard built-in Object prototype object.
The initial value of Promise.prototype.constructor
is the built-in Promise
constructor.
"then"
, (undefined, onRejected)).Note: The catch
function is intentionally generic; it does not require that its this value be a Promise object. Therefore, it can be transferred to other kinds of objects for use as a method.
Note: The then
function is not generic. If the this value is not an object with an [[IsPromise]] internal data property initialized to true, a TypeError exception is immediately thrown when it is called.
Promise instances are ordinary objects that inherit properties from the Promise prototype (the intrinsic, %PromisePrototype%). Promise instances are initially created with the internal properties described in this table.
Internal Data Property Name | Description |
---|---|
[[IsPromise]] | A branding property given to all promises at allocation-time. Uninitialized promises have it set to undefined, whereas initialized ones have it set to true. |
[[PromiseConstructor]] | The function object that was used to construct this promise. Checked by Promise.cast . |
[[Derived]] | A List of derived promise transforms that need to be processed once the promise's [[HasValue]] or [[HasReason]] become true. |
[[Following]] | Another promise that this one is following, or undefined. |
[[Value]] | The promise's direct fulfillment value (from resolving it with a non-thenable). Only meaningful if [[HasValue]] is true. |
[[HasValue]] | Whether the promise has a direct fulfillment value or not. This allows distinguishing between no direct fulfillment value, and one of undefined. |
[[Reason]] | The promise's direct rejection reason (from rejecting it). Only meaningful if [[HasReason]] is true. |
[[HasReason]] | Whether the promise has a direct rejection reason or not. This allows distinguishing between no direct rejection reason, and one of undefined. |
Add the following rows:
%Promise% | The initial value of the global object property named "Promise" . |
%PromisePrototype% | The initial value of the "prototype" data property of the intrinsic %Promise%. |
Add the following section:
Algorithm steps that say
mean the same things as:
To the extent possible under law,
Domenic Denicola
has waived all copyright and related or neighboring rights to
promises-unwrapping.
This work is published from:
United States
.