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


Promise Objects

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.

The ThenableCoercions Weak Map

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.:

Record Types for Promise Objects

The Derived Promise Transform Specification Type

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 Deferred Specification Type

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:

Abstract Operations for Promise Objects

GetDeferred ( C )

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.

  1. If IsConstructor(C) is false, throw a TypeError.
  2. Let resolver(passedResolve, passedReject) be an ECMAScript function that lets resolve be passedResolve and reject be passedReject.
  3. Let promise be the result of calling the [[Construct]] internal method of C with an argument list containing the single item resolver.
  4. ReturnIfAbrupt(promise).
  5. If IsPromise(promise) is false, throw a TypeError.
  6. Return the Deferred { [[Promise]]: promise, [[Resolve]]: resolve, [[Reject]]: reject }.

IsPromise ( x )

The abstract operation IsPromise checks for the promise brand on an object.

  1. If Type(x) is not Object, return false.
  2. If x does not have an [[IsPromise]] internal data property, return false.
  3. If the value of x's [[IsPromise]] internal data property is true, return true; otherwise, return false.

IsResolved ( p )

The abstract operation IsResolved checks for whether a promise's fate is resolved.

  1. If p's internal data property [[Following]] is not undefined, return true.
  2. If p's internal data property [[HasValue]] is true, return true.
  3. If p's internal data property [[HasReason]] is true, return true.
  4. Return false.

PropagateToDerived ( p )

The abstract operation PropagateToDerived propagates a promise's value or reason to all of its derived promises.

  1. Assert: exactly one of p's [[HasValue]] internal data property and p's [[HasReason]] internal data property is true.
  2. Let deriveds be the List that is the value of p's [[Derived]] internal data property.
  3. Repeat for each derived that is an element of deriveds, in original insertion order
    1. Let result be the result of calling UpdateDerived(derived, p).
    2. ReturnIfAbrupt(result).
  4. Set the value of p's [[Derived]] internal data property to a new empty List.

Reject ( p , r )

The abstract operation Reject rejects a promise with a reason.

  1. If IsResolved(p), return.
  2. Return the result of calling SetReason(p, r).

SetReason ( p , reason )

The abstract operation SetReason encapsulates the process of setting a promise's reason and then propagating this to any derived promises.

  1. Assert: the value of p's [[HasValue]] internal data property is false.
  2. Assert: the value of p's [[HasReason]] internal data property is false.
  3. Set the value of the [[Reason]] internal data property of p to reason.
  4. Set the value of the [[HasReason]] internal data property of p to true.
  5. Set the value of the [[Following]] internal data property of p to undefined.
  6. Return the result of calling PropagateToDerived(p).

SetValue ( p , value )

The abstract operation SetValue encapsulates the process of setting a promise's value and then propagating this to any derived promises.

  1. Assert: the value of p's [[HasValue]] internal data property is false.
  2. Assert: the value of p's [[HasReason]] internal data property is false.
  3. Set the value of the [[Value]] internal data property of p to value.
  4. Set the value of the [[HasValue]] internal data property of p to true.
  5. Set the value of the [[Following]] internal data property of p to undefined.
  6. Return the result of calling PropagateToDerived(p).

Then ( p, onFulfilled, onRejected )

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.

  1. Let following be the value of p's [[Following]] internal data property.
  2. If following is not undefined, return the result of calling Then(following, onFulfilled, onRejected).
  3. Let C be the result of calling Get(p, "constructor").
  4. ReturnIfAbrupt(C).
  5. Let deferred be the result of calling GetDeferred(C).
  6. ReturnIfAbrupt(deferred).
  7. Let returnedPromise be deferred.[[Promise]].
  8. Let derived be the Derived Promise Transform { [[DerivedPromise]]: returnedPromise, [[OnFulfilled]]: onFulfilled, [[OnRejected]]: onRejected }.
  9. Let result be the result of calling UpdateDerivedFromPromise(derived, p).
  10. ReturnIfAbrupt(result).
  11. Return returnedPromise.

ToPromise ( C , x )

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.

  1. If IsPromise(x) is true,
    1. Let constructor be the value of x's [[PromiseConstructor]] internal data property.
    2. If SameValue(constructor, C) is true, return x.
  2. Let deferred be the result of calling GetDeferred(C).
  3. ReturnIfAbrupt(deferred).
  4. Let resolve be deferred.[[Resolve]].
  5. If IsCallable(resolve) is false, throw a TypeError exception.
  6. Let result be the result of calling the [[Call]] internal method of resolve with undefined as thisArgument and a list containing x as argumentsList.
  7. ReturnIfAbrupt(result).
  8. Return deferred.[[Promise]].

Resolve ( p , x )

The operator Resolve resolves a promise with a value.

  1. If IsResolved(p), return.
  2. If IsPromise(x) is true,
    1. If SameValue(p, x),
      1. Let selfResolutionError be a newly-created TypeError object.
      2. Call SetReason(p, selfResolutionError).
    2. Otherwise, if x.[[Following]] is not undefined,
      1. Set p.[[Following]] to x.[[Following]].
      2. Append { [[DerivedPromise]]: p, [[OnFulfilled]]: undefined, [[OnRejected]]: undefined } as the last element of x.[[Following]].[[Derived]].
    3. Otherwise, if x.[[HasValue]] is true, call SetValue(p, x.[[Value]]).
    4. Otherwise, if x.[[HasReason]] is true, call SetReason(p, x.[[Reason]]).
    5. Otherwise,
      1. Set p.[[Following]] to x.
      2. Append { [[DerivedPromise]]: p, [[OnFulfilled]]: undefined, [[OnRejected]]: undefined } as the last element of x.[[Derived]].
  3. Otherwise, call SetValue(p, x).

UpdateDerived ( derived , originator )

The operator UpdateDerived propagates a promise's state to a single derived promise using any relevant transforms.

  1. Assert: exactly one of originator.[[HasValue]] and originator.[[HasReason]] is true.
  2. If originator.[[HasValue]] is true,
    1. If Type(originator.[[Value]]) is Object, queue a microtask to run the following:
      1. If ThenableCoercions.has(originator.[[Value]]),
        1. Let coercedAlready be ThenableCoercions.get(originator.[[Value]]).
        2. Call UpdateDerivedFromPromise(derived, coercedAlready).
      2. Otherwise,
        1. Let thenResult be Get(originator.[[Value]], "then").
        2. If thenResult is an abrupt completion, call UpdateDerivedFromReason(derived, thenResult.[[value]]).
        3. Otherwise, if IsCallable(thenResult.[[value]]),
          1. Let coerced be CoerceThenable(originator.[[Value]], thenResult.[[value]]).
          2. Call UpdateDerivedFromPromise(derived, coerced).
        4. Otherwise, call UpdateDerivedFromValue(derived, originator.[[Value]]).
    2. Otherwise, call UpdateDerivedFromValue(derived, originator.[[Value]]).
  3. Otherwise, call UpdateDerivedFromReason(derived, originator.[[Reason]]).

UpdateDerivedFromValue ( derived , value )

The operator UpdateDerivedFromValue propagates a value to a derived promise, using the relevant onFulfilled transform if it is callable.

  1. If IsCallable(derived.[[OnFulfilled]]), call CallHandler(derived.[[DerivedPromise]], derived.[[OnFulfilled]], value).
  2. Otherwise, call SetValue(derived.[[DerivedPromise]], value).

UpdateDerivedFromReason ( derived , reason )

The operator UpdateDerivedFromReason propagates a reason to a derived promise, using the relevant onRejected transform if it is callable.

  1. If IsCallable(derived.[[OnRejected]]), call CallHandler(derived.[[DerivedPromise]], derived.[[OnRejected]], reason).
  2. Otherwise, call SetReason(derived.[[DerivedPromise]], reason).

UpdateDerivedFromPromise ( derived , promise )

The operator UpdateDerivedFromPromise propagates one promise's state to the derived promise, using the relevant transform if it is callable.

  1. If promise.[[HasValue]] is true or promise.[[HasReason]] is true, call UpdateDerived(derived, promise).
  2. Otherwise, append derived as the last element of promise.[[Derived]].

CallHandler ( derivedPromise , handler , argument )

The operator CallHandler applies a transformation to a value or reason and uses it to update a derived promise.

  1. Queue a microtask to do the following:
    1. Let result be handler.[[Call]](undefined, (argument)).
    2. If result is an abrupt completion, call Reject(derivedPromise, result.[[value]]).
    3. Otherwise, call Resolve(derivedPromise, result.[[value]]).

CoerceThenable ( thenable , then )

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.

  1. Assert: Type(thenable) is Object.
  2. Assert: IsCallable(then).
  3. Assert: the execution context stack is empty.
  4. Let p be the result of calling PromiseCreate().
  5. Let resolve(x) be an ECMAScript function that calls Resolve(p, x).
  6. Let reject(r) be an ECMAScript function that calls Reject(p, r).
  7. Let result be then.[[Call]](thenable, (resolve, reject)).
  8. If result is an abrupt completion, call Reject(p, result.[[value]]).
  9. Call ThenableCoercions.set(thenable, p).
  10. Return p.

The Promise Constructor

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.

Promise ( resolver )

  1. Let promise be the this value.
  2. If Type(promise) is not Object, then throw a TypeError exception.
  3. If promise does not have an [[IsPromise]] internal data property, then throw a TypeError exception.
  4. If promise's [[IsPromise]] internal data property is not undefined, then throw a TypeError exception.
  5. Return the result of calling PromiseInitialise(promise, resolver).

new Promise ( ... argumentsList )

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:

  1. Let F be the Promise function object on which the new operator was applied.
  2. Let argumentsList be the argumentsList argument of the [[Construct]] internal method that was invoked by the new operator.
  3. Return the result of OrdinaryConstruct(F, argumentsList).

If Promise is implemented as an ordinary function object, its [[Construct]] internal method will perform the above steps.

Abstract Operations for the Promise Constructor

PromiseAlloc ( constructor )

  1. Let obj be the result of calling OrdinaryCreateFromConstructor(constructor, "%PromisePrototype%", ([[IsPromise]], [[PromiseConstructor]], [[Derived]], [[Following]], [[Value]], [[HasValue]], [[Reason]], [[HasReason]])).
  2. Set obj's [[PromiseConstructor]] internal data property to constructor.
  3. Return obj.

PromiseInitialise ( obj, resolver )

  1. If IsCallable(resolver) is false, then throw a TypeError exception.
  2. Set obj's [[IsPromise]] internal data property to true.
  3. Set obj's [[Derived]] internal data property to a new empty List.
  4. Set obj's [[HasValue]] internal data property to false.
  5. Set obj's [[HasReason]] internal data property to false.
  6. Let resolve(x) be an ECMAScript function that calls Resolve(obj, x).
  7. Let reject(r) be an ECMAScript function that calls Reject(obj, r).
  8. Let result be the result of calling the [[Call]] internal method of resolver with undefined as thisArgument and a List containing resolve and reject as argumentsList.
  9. If result is an abrupt completion, call Reject(obj, result.[[value]]).
  10. Return obj.

PromiseCreate ( )

The abstract operation PromiseCreate is used by the specification to create new promise objects in the pending state.

  1. Let obj be the result of calling PromiseAlloc(%Promise%).
  2. ReturnIfAbrupt(obj).
  3. Let resolver be a new, empty ECMAScript function object.
  4. Return the result of calling PromiseInitialise(obj, resolver).

Properties of the Promise Constructor

Promise [ @@create ] ( )

  1. Let F be the this value.
  2. Return the result of calling PromiseAlloc(F).

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

Promise.all ( iterable )

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.

  1. Let C be the this value.
  2. Let deferred be the result of calling GetDeferred(C).
  3. ReturnIfAbrupt(deferred).
  4. Let resolve be deferred.[[Resolve]].
  5. If IsCallable(resolve) is false, throw a TypeError exception.
  6. Let iterator be the result of calling GetIterator(iterable).
  7. RejectIfAbrupt(iterator, deferred).
  8. Let values be the result of calling ArrayCreate(0).
  9. Let countdown be 0.
  10. Let index be 0.
  11. Repeat
    1. Let next be the result of calling IteratorStep(iterator).
    2. RejectIfAbrupt(next, deferred).
    3. If next is false,
      1. If index is 0,
        1. Let result be the result of calling the [[Call]] internal method of resolve with undefined as thisArgument and a list containing values as argumentsList.
        2. ReturnIfAbrupt(result).
      2. Return deferred.[[Promise]].
    4. Let nextValue be the result of calling IteratorValue(next).
    5. RejectIfAbrupt(nextValue, deferred).
    6. Let nextPromise be the result of calling ToPromise(C, nextValue).
    7. RejectIfAbrupt(nextPromise, deferred).
    8. Let currentIndex be the current value of index.
    9. Let onFulfilled(v) be an ECMAScript function that:
      1. Calls the [[DefineOwnProperty]] internal method of values with arguments currentIndex and Property Descriptor { [[Value]]: v, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
      2. Sets countdown to countdown - 1.
      3. If countdown is 0, calls resolve.[[Call]](undefined, (values)).
    10. Let result be the result of calling Then(nextPromise, onFulfilled, deferred.[[Reject]]).
    11. RejectIfAbrupt(result, deferred).
    12. Set index to index + 1.
    13. Set countdown to countdown + 1.

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.

Promise.cast ( x )

cast coerces its argument to a promise, or returns the argument if it is already a promise.

  1. Let C be the this value.
  2. Return the result of calling ToPromise(C, x).

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.

Promise.race ( iterable )

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.

  1. Let C be the this value.
  2. Let deferred be the result of calling GetDeferred(C).
  3. ReturnIfAbrupt(deferred).
  4. Let iterator be the result of calling GetIterator(iterable).
  5. RejectIfAbrupt(iterator, deferred).
  6. Repeat
    1. Let next be the result of calling IteratorStep(iterator).
    2. RejectIfAbrupt(next, deferred).
    3. If next is false, return deferred.[[Promise]].
    4. Let nextValue be the result of calling IteratorValue(next).
    5. RejectIfAbrupt(nextValue, deferred).
    6. Let nextPromise be the result of calling ToPromise(C, nextValue).
    7. RejectIfAbrupt(nextPromise, deferred).
    8. Let result be the result of calling Then(nextPromise, deferred.[[Resolve]], deferred.[[Reject]]).
    9. RejectIfAbrupt(result, deferred).

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.

Promise.reject ( r )

reject returns a new promise rejected with the passed argument.

  1. Let C be the this value.
  2. Let deferred be the result of calling GetDeferred(C).
  3. ReturnIfAbrupt(deferred).
  4. Let reject be deferred.[[Reject]].
  5. If IsCallable(reject) is false, throw a TypeError exception.
  6. Let result be the result of calling the [[Call]] internal method of reject with undefined as thisArgument and a list containing r as argumentsList.
  7. ReturnIfAbrupt(result).
  8. Return deferred.[[Promise]].

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.

Promise.resolve ( x )

resolve returns a new promise resolved with the passed argument.

  1. Let C be the this value.
  2. Let deferred be the result of calling GetDeferred(C).
  3. ReturnIfAbrupt(deferred).
  4. Let resolve be deferred.[[Resolve]].
  5. If IsCallable(resolve) is false, throw a TypeError exception.
  6. Let result be the result of calling the [[Call]] internal method of resolve with undefined as thisArgument and a list containing x as argumentsList.
  7. ReturnIfAbrupt(result).
  8. Return deferred.[[Promise]].

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.

Properties of the Promise Prototype Object

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.

Promise.prototype.constructor

The initial value of Promise.prototype.constructor is the built-in Promise constructor.

Promise.prototype.catch ( onRejected )

  1. Let promise be the this value.
  2. Return the result of calling Invoke(promise, "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.

Promise.prototype.then ( onFulfilled , onRejected )

  1. Let promise be the this value.
  2. If IsPromise(promise) is false, throw a TypeError exception.
  3. Return the result of calling Then(promise, onFulfilled, onRejected).

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.

Properties of Promise Instances

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 Properties of Promise Instances
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.

Deltas to Other Areas of the Spec

Well-Known Intrinsic Objects Table

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%.

The Completion Record Specification Type

Add the following section:

RejectIfAbrupt

Algorithm steps that say

  1. RejectIfAbrupt(argument, deferred).

mean the same things as:

  1. If argument is an abrupt completion,
    1. Let reject be deferred.[[Reject]].
    2. If IsCallable(reject) is false, throw a TypeError exception.
    3. Let result be the result of calling the [[Call]] internal method of reject with undefined as thisArgument and a list containing argument.[[value]] as argumentsList.
    4. ReturnIfAbrupt(result).
    5. Return deferred.[[Promise]].
  2. Else if argument is a Completion Record, then let argument be argument.[[value]].

CC0
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 .