Promises/KISS

From CommonJS Spec Wiki
Jump to: navigation, search

After having created the library FuturesJS and after using it for a while in the Browser and with Node.js I'd like to submit Promises/KISS to CommonJS for consideration.

This is the roughly direction which I intend to take Futures 2.0.

Example Usage: (view with syntax http://gist.github.com/646206)

   var Promise = require('futures');
   
   function getUnavailableData(params) {
     var p = Promise.create({ updatable: true, timeout: Infinity, freeze: false });
     // p.fulfill(err, data, data, ...);
     setInterval(askUserHowHeFeelsAboutLife, 60*60*1000, p.fulfill);
     return p.protected(); // returns a different object which has no fulfill method
   }
   
   function useUnavailabledata() {
     var p = getUnavailableData();
     if (! (p instanceof Promise)) {
       throw new Error("How odd, not a promise instance");
     }
     if ('function' !== typeof p.when) {
       throw new Error("How odd, not a promise by duck-typing");
     }
     p.when(function (err, data, data, ...) {
       // store data in cache
     });
     if (p.options.updatable) {
       p.whenever(function (err, data, data, ...) {
         // update data in cache
       });
     }
     p.ready // true once resolved
   }
   
   var j = Promise.join(options);
   j.add(p1, p2, p3);
   //or
   j.add([p1, p2, p3]);



Argument:

The point of a promise is to immediately return data which is not immediately available. The essence of a promise is a callback function.

The benefits of a promise which distinguishes it from a normal callback

  • the user can determine that it is a promise
  • a particular set of data is associated with it
  • multiple interested parties can use that data
  • it can be easily joined with other promises


The specification should be simple enough to be

  • easily understood
  • implemented in the Browser
  • implemented in a few days
  • used as a drop-in replacement in for current callbacks methods of doing the same thing.
  • progressively enhanced
  • extended by vendors without severe compatibility issues
  • good enough

In the spirit of "Getting Real", why not put a deadline on it: Proposal will be chosen by xDay of xMonth and pick what works well and cut features and specs so that something is available by the deadline. Then work on a 2.0 with another deadline.




Specification / API:

  • vendor may name the module
  • Ex: var Promise = require('myPromiseLib');
  • vendor may choose how to create the promise
  • Ex: Promise.create(options) or new Promise(options)
  • Promise must be 'self-documenting' (enumerable methods, rather than being lost in the global namespace)
  • Promise must be duck-type detectable.
  • Ex: if (p.when) {//is promise}
  • Ex: if (p.whenever) {//is updatable promise}
  • Ex: if (p.options.cow) {// has supercow powers from vendor x}
  • Promise should be instanceof detectable (see example in gist above)
  • The instantiation of the promise must allow for optional parameters, at least:
 * timeout. If the promise isn't resolved in this number of ms the 'timeout' error is issued with whatever data is available
           the default value should be set to 10 seconds
           The user should always set the timeout value. It is 10 seconds by default
 * updatable. If the promise will accept multiple resolutions.
 * freeze. By default all promised items will be frozen or copied, but this may be disabled
  • `when` and `whenever` must allow multiple callbacks to be registered
 * the second argument to `when` is reserved for a params hash
 * 'Err', which may be any type of literal, array, object or exception must be the first argument passed to the function registered with `when` or `whenever`
           This promotes good coding: The less work it is for someone to handle an error, the more likely they are to handle it. From my own experience I've found that I handle errors much more often in Node than I did in jQuery because the convention is to hand it back as the first param
  • `join` should join multiple promises in the order in which they are passed in.
  • If updatable
 * will resolve every time that each promise has resolved at least once
 * will throw if the promises added are not updatable
  • Ex: var j = Promise.join({updatable: true});
     j.add(p1, p2, p3); // also accepts array j.add([p1, p2, p3])
     j.add(p4); // throws exception
     j.when(function (args1, args2, args3) {});


Many things - like get, post, put, del can be implemented atop the promise. Something such as an update handler could be included in a future version.

The lowfat version of this spec would be to remove the "updatable"/"whenever", but definitely leave the join and leave the options for vendor extensions.

To me it seems that a promises spec without a join spec isn't worth the effort - it's just a formalization of a callback.