Promises

From CommonJS Spec Wiki
Jump to: navigation, search

STATUS: PROPOSAL, DISCUSSION

Promises

Promises provide a well-defined interface for interacting with an object that represents the result of an action that is performed asynchronously, and may or may not be finished at any given point in time. By utilizing a standard interface, different components can return promises for asynchronous actions and consumers can utilize the promises in a predictable manner. Promises can also provide the fundamental entity to be leveraged for more syntactically convenient language-level extensions that assist with asynchronicity.

Prior Art

  • Dojo's Deferred A form of promises that was inherited from MochiKit and Twisted.
  • ref_send. A promise manager.
  • [1]. E reference API
  • [2]. FuturesJS asynchronous toolkit

Proposals

Requirements

  • 1. if a function receives a promise as its input, that function must itself return a promise for its output. Provide a simple mechanism that guarantees, in the face of programmer error, non-determinism, and malice:
  • 1.1. that defends the function's internal consistency from attack by the provider of the input promise.
  • 1.2. that defends the function's internal consistency from attack by the consumer of the output promise.
  • 1.3 that defends each recipient of the output promise from each other recipient.
  • 1.4 that prevents the input promise from being leaked to the output consumer. The return value of this expression must never be the input or provide the consumer from gaining direct access to the input.
function API(input) {
    return when(input, function (input) {
    });
};

For example, a password checking API must be able to retain a reference to a remote table of password hashes and must never give the consumer direct access to any of those hashes.

function PasswordChecker(accounts) {
    return function (user, password) {
        var passwordHash = Q.get(accounts, user);
        return when(passwordHash, function (passwordHash) {
            if (hash(password) === passwordHash)
                return true;
        };
    };
}
// returns a password checking capability
// but does not return access to the passwordHash
return PasswordChecker(accounts);
  • 1.4. when the input is resolved, and if it is discovered that there is additional unresolved input, permit the function to forward a new promise.
  • 1.5. that permits the function to either forward the failure of its input, or recover from that failure by forwarding a resolved value, or recover by forwarding a new promise to eventually resolve or fail later.
  • 2. avoid interleaving hazards.
  • 2.1. prevent promise consumers from giving promise providers direct use of their callbacks as in these counter-examples:
var internalCounter = 0;

// /!\ HAZARD: untrusted promise given direct access to a
// function that has the capability to alter internal
// state, either during the same turn or in any future turn
promise("when", function () {
    internalCounter++;
});

// /!\ HAZARD: untrusted promise given direct access to a
// function that has the capability to alter internal
// state, either during the same turn or in any future turn
promise.when(function () {
    internalCounter++;
});

// /!\ HAZARD: untrusted promise given direct access to a
// function that has the capability to alter internal
// state, either during the same turn or in any future turn
promise.then(function () {
    internalCounter++;
});

ASSERT.equal(internalCounter, 0);
  • 2.2. prevent promise providers from calling a consumer's internal callbacks in the same turn as they are registered.
// /!\ HAZARD: untrusted promise given direct access to a
// function that has the capability to alter internal
// state, either during the same turn or in any future turn
var internalCounter = 0;
when(promise, function () {
    internalCounter++;
});
ASSERT.equal(internalCounter, 0);
  • 2.3. prevent promise providers from calling a consumer's internal callback or errback more than once, ever.
var internalCounter = 0;
when(promise, function (value) {
    internalCounter++;
}, function (error) {
    internalCounter++;
});
setTimeout(function () {
    ASSERT.ok(internalCounter == 0 || internalCounter == 1);
}, forever);
  • 3. Separate the concerns of "resolving" from "observing resolution".
  • 3.1 permit an API to safely produce a "deferred", retain the "resolver" portion for itself, and distribute the "promise" component to any number of mutually suspicious consumers.
  • 3.2 "Competitive Races": permit an API to safely produce a "deferred", retain the "promise" portion for itself, and distribute the "resolver" component to any number of mutually suspicious consumers.
  • 4. provide a mechanism that permits a promise consumer to throw an exception in any turn where a suspicious promise attempts to resolve multiple times.
  • 5. Provide support for arbitrary message passing to promises, including support for intercepting arbitrary messages for which no handler has been explicitly provided.


Related Discussions