Skip to content

Instantly share code, notes, and snippets.

@Gozala
Created November 23, 2010 13:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gozala/711766 to your computer and use it in GitHub Desktop.
Save Gozala/711766 to your computer and use it in GitHub Desktop.
Proposal to unify promise specs.

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.

Specification

  • Promise()

    Module exports Promise function. All the promises MUST derive from Promise.prototype. Exported Promise can be used to identify Promise objects:

      object.valueOf() instanceof Promise
    

    Specification recognizes three states of the Promise instances:

    • pending

      Instance of Promise is pending if following evaluates to true:

         promise.valueOf().valueOf() === promise.valueOf()
      
    • fulfilled

      Instance of Promise is fulfilled if following evaluates to true:

          !(promise.valueOf().valueOf() instanceof require('promise').Promise)
      
    • broken

      Instance of Promise is broken if following evaluates to true:

          promise.valueOf() instanceof require('promise').Broken
      

    The promise may only move from pending to fulfilled or from pending to broken and only once. Regardless whether promise was fulfilled or broken it's value MUST not be changed, just as a values in JavaScript, primitives and object identities, can not change (although objects themselves may always be mutable even if there identity isn't).

  • Promise.make()

    Returns an object containing a new promise:

    • promise

      Property is instance of Promise and represents newly created promise in pending state.

    • emit(String(event), args...)

      Function emits events for a listeners of the bound promise. Events MAY be emitted to transition bound promise from one state into another. Emitting events that are not recognized by this spec is absolutely legal while promise is in pending state, in fact it is expected that events like "progress" may be emitted by promises that represent forEach-able streams.

      If emit is called for a pending promise:

      • With "fulfill" first argument & instance of Broken second argument promise will transition to a broken state. Emitting any events for this promise will have no effect, neither listeners will be notified.
      • With "fulfill" first argument & instance of Promise second argument promise will stay in pending state and will be automatically fulfilled with a value of a promise that was passed as a second argument, once it will be fulfilled. Meanwhile all the events emitted for a fulfilling promise will be forwarded to listeners of this one.
      • With "fulfill" first argument & anything other then instance of Broken and instance of Promise second promise will transition to fulfilled state. Emitting any events for this promise will have no effect, neither listeners will be notified.
      • With "brake" first argument and anything for a second argument, then instance of Broken is created with a second argument and promise is fulfilled with it, that will transitions promise to the broken state. Emitting any events for this promise will have no effect, neither listeners will be notified.

      When events are emitted for promise all their registered listeners for that event are are called in the next turn of event loop with a promise first argument and all the arguments passed to the emit function starting from second as following ones.

  • Broken(object)

    Module exports Broken class that derives form Error and may be used to identify promises that got broken. Instances may be created by calling this function with or without new keyword. If instance of Broken is passed to the constructor it will be immediately returned. If not all the properties of the object will be copied to the instance. Constructor also accepts strings that will be exposed as a message property of returned instance.

  • on(promise, String(event), listener)

Exported function is used to register event listeners to the specified event on for the specified promise. Non Promise objects or promises that are in non pending state will be ignored.

@Gozala
Copy link
Author

Gozala commented Nov 23, 2010

Example of thenable promises on top of this.

var promises = require('promise'), Promise = promises.Promise, on = promises.on

ThenablePromiseDescriptor =
{ then: { value: function then(onFulfill, onBrake, onProgress) {
    var deferred = Promise.make()
    ,   promise = Object.create(deferred.promise, ThenablePromiseDescriptor)
    on(this, 'fulfill', function fulfilled(promise, value) {
      deferred.emit('fulfill', onFulfill())
    })
    on(this, 'brake', function broked(promise, value) {
      deferred.emit('fulfill', onBrake(value))
    })
    on(this, 'progress', function progressed(value) {
      deferred.emit('progressed', onProgress(value))
    })
  }}
}
function ThenablePromise(promise) {
   return Object.create(promies, ThenablePromiseDescriptor)
}

@Gozala
Copy link
Author

Gozala commented Nov 23, 2010

Example of when like promises

var promises = require('promise'), Promise = promises.Promise, on = promises.on
,   enqueue = require('event-queue').enqueue

var DeferredDescriptor =
{ resolve: { value: function resolve(value) {
    this.emit('fulfill', value)
  }
, reject: { value: function reject(reason) {
    this.emit('brake', reason)
  }
}
function defer() {
  return Object.create(Promise.make(), DeferredDescriptor)
}

function when(promise, onResolved, onReject) {
  var deferred = defer()
  if (promise instanceof Promise) {
    if (promise.valueOf() instanceof Promise) { // pending
      on('fulfill', promise, function resolved(value) {
        deferred.emit('fulfill', onResolved(value))
      })
      on('broken', promise, function rejected(reason) {
        deferred.emit('fulfill', onReject ? onReject(reason) : reason)
      })
    } else if (promise.valueOf() instanceof Broken) { // broken
      enqueue(function() {
        deferred.emit('fulfill', onReject ? onReject(promise.valueOf()) : promise.valueOf())
      })
    } else { // fulfilled
      enqueue(function() {
        deferred.emit('fulfill', onResolved(promise.valueOf()))
      })
    }
  } else {
    enqueue(function() {
      deferred.emit('fulfill', onResolved(promise.valueOf()))
    })
  }
  return defer.promise
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment