      1 var hitch = require("./base/functions").hitch,
      2     define = require("./define").define, base = require("./base");
      3
      4 var Promise = define(null, {
      5     instance : {
      6         /** @lends comb.Promise.prototype */
      7         __fired : false,
      8
      9         __results : null,
     10
     11         __error : null,
     12
     13         __errorCbs : null,
     14
     15         __cbs : null,
     16
     17         /**
     18          * Promise object used for handling a thread
     19          *@example
     20          *          var myFunc = function(){
     21          *              var promise = new Promise();
     22          *              //callback the promise after 10 Secs
     23          *              setTimeout(hitch(promise, "callback"), 10000);
     24          *              return promise;
     25          *          }
     26          *          var myFunc2 = function(){
     27          *              var promises =[];
     28          *              for(var i = 0; i < 10; i++){
     29          *                  promises.push(myFunc);
     30          *              }
     31          *              //create a new promise list with all 10 promises
     32          *              return new PromiseList(promises);
     33          *          }
     34          *
     35          *          myFunc.then(do something...)
     36          *          myFunc.addCallback(do something...)
     37          *          myFunc.cain(myfunc).then(do something...)
     38          *          myFunc.cain(myfunc).addCallback(do something...)
     39          *
     40          *          myFunc2.then(do something...)
     41          *          myFunc2.addCallback(do something...)
     42          *          myFunc2.cain(myfunc).then(do something...)
     43          *          myFunc2.cain(myfunc).addCallback(do something...)
     44          *  @memberOf comb
     45          * @constructs
     46          */
     47         constructor : function() {
     48             this.__errorCbs = [];
     49             this.__cbs = [];
     50         },
     51         /**
     52          * @private
     53          */
     54         __resolve : function() {
     55             if (!this.__fired) {
     56                 this.__fired = true;
     57                 var cbs = this.__error ? this.__errorCbs : this.__cbs,
     58                     len = cbs.length, i,
     59                     results = this.__error || this.__results;
     60                 for (i = 0; i < len; i++) {
     61                     cbs[i].apply(this, results);
     62                 }
     63             }
     64         },
     65
     66         /**
     67          * Add a callback to the callback chain of the promise
     68          *
     69          *
     70          * @param {Function} cb the function to callback when the promise is resolved
     71          */
     72         addCallback : function(cb) {
     73             if (cb) {
     74                 if (this.__fired && this.__results) {
     75                     cb.apply(this, this.__results);
     76                 } else {
     77                     this.__cbs.push(cb);
     78                 }
     79             }
     80             return this;
     81         },
     82
     83
     84         /**
     85          * Add a callback to the errback chain of the promise
     86          *
     87          * @param {Function} cb the function to callback when the promise errors
     88          */
     89         addErrback : function(cb) {
     90             if (cb) {
     91                 if (this.__fired && this.__error) {
     92                     cb.apply(this, this.__error);
     93                 } else {
     94                     this.__errorCbs.push(cb);
     95                 }
     96             }
     97         },
     98
     99         both : function(cb) {
    100             this.addCallback(cb);
    101             this.addErrback(cb);
    102             return this;
    103         },
    104
    105         /**
    106          * When called all functions registered as callbacks are called with the passed in results.
    107          *
    108          * @param anything variable number of results to pass back to listeners of the promise
    109          */
    110         callback : function() {
    111             if (this.__fired) {
    112                 throw new Error("Already fired!");
    113             }
    114             this.__results = Array.prototype.slice.call(arguments);
    115             this.__resolve();
    116             return this;
    117         },
    118
    119         /**
    120          * When called all functions registered as errbacks are called with the passed in error(s)
    121          *
    122          * @param anything variable number of errors to pass back to listeners of the promise
    123          */
    124         errback : function(i) {
    125             if (this.__fired) {
    126                 throw new Error("Already fired!");
    127             }
    128             this.__error = Array.prototype.slice.call(arguments);
    129             this.__resolve();
    130             return this;
    131         },
    132
    133         /**
    134          * Call to specify action to take after promise completes or errors
    135          *
    136          * @param {Function} [callback=null] function to call after the promise completes successfully
    137          * @param {Function} [errback=null] function to call if the promise errors
    138          */
    139         then : function(callback, errback) {
    140             this.addCallback(callback);
    141             this.addErrback(errback);
    142             return this;
    143         },
    144
    145         /**
    146          * Call to chaining of promises
    147          * @param callback method to call that returns a promise to call after this one completes.
    148          * @param errback method to call if this promise errors.
    149          */
    150         chain : function(callback, errback) {
    151             var promise = new Promise();
    152             this.addCallback(function(results) {
    153                 callback.call(this, results).then(hitch(promise, "callback"), hitch(promise, "errback"));
    154             });
    155             this.addErrback(errback);
    156             return promise;
    157         }
    158
    159     }
    160 });
    161
    162
    163 var PromiseList = define(Promise, {
    164     instance : {
    165         /** @lends comb.PromiseList.prototype */
    166
    167         /*@private*/
    168         __results : null,
    169
    170         /*@private*/
    171         __errors : null,
    172
    173         /*@private*/
    174         __promiseLength : 0,
    175
    176         /*@private*/
    177         __defLength : 0,
    178
    179         /*@private*/
    180         __firedLength : 0,
    181
    182         /**
    183          *
    184          *  PromiseList object used for handling a list of Promises
    185          * @example         var myFunc = function(){
    186          *              var promise = new Promise();
    187          *              //callback the promise after 10 Secs
    188          *              setTimeout(hitch(promise, "callback"), 10000);
    189          *              return promise;
    190          *          }
    191          *          var myFunc2 = function(){
    192          *              var promises =[];
    193          *              for(var i = 0; i < 10; i++){
    194          *                  promises.push(myFunc);
    195          *              }
    196          *              //create a new promise list with all 10 promises
    197          *              return new PromiseList(promises);
    198          *          }
    199          *          var pl = new comb.PomiseList([myFunc(), myFunc2()]);
    200          *          pl.then(do something...)
    201          *          pl.addCallback(do something...)
    202          *          pl.cain(myfunc).then(do something...)
    203          *          pl.cain(myfunc).addCallback(do something...)
    204          *
    205          *  @param {comb.Promise[]} [defs=[]] the list of promises
    206          * @constructs
    207          * @augments comb.Promise
    208          * @memberOf comb
    209          * */
    210         constructor : function(defs) {
    211             this.__defLength = defs.length;
    212             this.__errors = [];
    213             this.__results = [];
    214             this.super(arguments);
    215             defs.forEach(this.__addPromise, this);
    216         },
    217
    218         /**
    219          * Add a promise to our chain
    220          * @private
    221          * @param promise the promise to add to our chain
    222          * @param i the index of the promise in our chain
    223          */
    224         __addPromise : function(promise, i) {
    225             promise.addCallback(hitch(this, function() {
    226                 var args = Array.prototype.slice.call(arguments);
    227                 args.unshift(i);
    228                 this.callback.apply(this, args);
    229             }));
    230             promise.addErrback(hitch(this, function() {
    231                 var args = Array.prototype.slice.call(arguments);
    232                 args.unshift(i);
    233                 this.errback.apply(this, args);
    234             }));
    235         },
    236
    237         /**
    238          * Resolves the promise
    239          * @private
    240          */
    241         __resolve : function() {
    242             if (!this.__fired) {
    243                 this.__fired = true;
    244                 var cbs = this.__errors.length ? this.__errorCbs : this.__cbs,
    245                     len = cbs.length, i,
    246                     results = this.__errors.length ? this.__errors : this.__results;
    247                 for (i = 0; i < len; i++) {
    248                     cbs[i].call(this, results);
    249                 }
    250             }
    251         },
    252
    253         addCallback : function(cb) {
    254             if (cb) {
    255                 if (this.__fired && !this.__errors.length) {
    256                     cb.call(this, this.__results);
    257                 } else {
    258                     this.__cbs.push(cb);
    259                 }
    260             }
    261             return this;
    262         },
    263
    264         addErrback : function(cb) {
    265             if (cb) {
    266                 if (this.__fired && this.__errors.length) {
    267                     cb.call(this, this.__errors);
    268                 } else {
    269                     this.__errorCbs.push(cb);
    270                 }
    271             }
    272             return this;
    273         },
    274
    275
    276         callback : function(i) {
    277             if (this.__fired) {
    278                 throw new Error("Already fired!");
    279             }
    280             this.__results[i] = (Array.prototype.slice.call(arguments));
    281             this.__firedLength++;
    282             if (this.__firedLength == this.__defLength) {
    283                 this.__resolve();
    284             }
    285             return this;
    286         },
    287
    288
    289         errback : function(i) {
    290             if (this.__fired) {
    291                 throw new Error("Already fired!");
    292             }
    293             this.__errors[i] = Array.prototype.slice.call(arguments);
    294             this.__firedLength++;
    295             if (this.__firedLength == this.__defLength) {
    296                 this.__resolve();
    297             }
    298             return this;
    299         }
    300
    301     }
    302 });
    303 exports.Promise = Promise;
    304 exports.PromiseList = PromiseList;