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;