Promise async wrappers patch
[contrib/cloudeebus.git] / src / js / cloudeebus-promise.js
1 /******************************************************************************
2  * Copyright 2012 - 2013 Intel Corporation.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *****************************************************************************/
16
17
18
19 /*****************************************************************************/
20
21 function _processWrappers(wrappers, value) {
22         for (var i=0; i<wrappers.length; i++)
23                 wrappers[i](value);
24 }
25
26
27 function _processWrappersAsync(wrappers, value) {
28         var taskid = -1;
29         function processAsyncOnce() {
30                 if (!wrappers.processed)
31                         _processWrappers(wrappers, value);
32                 clearInterval(taskid);
33                 wrappers.processed = true;
34         }
35         taskid = setInterval(processAsyncOnce, 200);
36 }
37
38
39
40 /*****************************************************************************/
41
42 cloudeebus.PromiseResolver = function(promise) {
43         this.promise = promise;
44         this.resolved = null;
45     return this;
46 };
47
48
49 cloudeebus.PromiseResolver.prototype.resolve = function(value, sync) {
50         if (this.resolved)
51                 return;
52         
53         var then = (value && value.then && value.then.apply) ? value.then : null;
54         if (then) {
55                 var self = this;                
56                 var fulfillCallback = function(arg) {
57                         self.resolve(arg, true);
58                 };      
59                 var rejectCallback = function(arg) {
60                         self.reject(arg, true);
61                 };
62                 try {
63                         then.apply(value, [fulfillCallback, rejectCallback]);
64                 }
65                 catch (e) {
66                         this.reject(cloudeebus.getError(e), true);
67                 }
68         }
69         
70         this.fulfill(value, sync);
71 };
72
73
74 cloudeebus.PromiseResolver.prototype.fulfill = function(value, sync) {
75         if (this.resolved)
76                 return;
77         
78         var promise = this.promise;
79         promise.state = "fulfilled";
80         promise.result = value;
81         
82         this.resolved = true;
83         if (sync)
84                 _processWrappers(promise._fulfillWrappers, value);
85         else
86                 _processWrappersAsync(promise._fulfillWrappers, value);
87 };
88
89
90 cloudeebus.PromiseResolver.prototype.reject = function(value, sync) {
91         if (this.resolved)
92                 return;
93         
94         var promise = this.promise;
95         promise.state = "rejected";
96         promise.result = value;
97         
98         this.resolved = true;
99         if (sync)
100                 _processWrappers(promise._rejectWrappers, value);
101         else
102                 _processWrappersAsync(promise._rejectWrappers, value);
103 };
104
105
106
107 /*****************************************************************************/
108
109 cloudeebus.Promise = function(init) {
110         this.state = "pending";
111         this.result = null;
112         this._fulfillWrappers = [];
113         this._rejectWrappers = [];
114         this.resolver = new cloudeebus.PromiseResolver(this);
115         if (init) {
116                 try {
117                         init.apply(this, [this.resolver]);
118                 }
119                 catch (e) {
120                         this.resolver.reject(cloudeebus.getError(e), true);
121                 }
122         }
123     return this;
124 };
125
126
127 cloudeebus.Promise.prototype.appendWrappers = function(fulfillWrapper, rejectWrapper) {
128         if (fulfillWrapper)
129                 this._fulfillWrappers.push(fulfillWrapper);
130         if (rejectWrapper)
131                 this._rejectWrappers.push(rejectWrapper);
132         if (this.state == "fulfilled")
133                 _processWrappersAsync(this._fulfillWrappers, this.result);
134         if (this.state == "rejected")
135                 _processWrappersAsync(this._rejectWrappers, this.result);
136 };
137
138
139 cloudeebus.Promise.prototype.then = function(fulfillCB, rejectCB) {
140         var promise = new cloudeebus.Promise();
141         var resolver = promise.resolver;
142         var fulfillWrapper, rejectWrapper;
143         
144         if (fulfillCB)
145                 fulfillWrapper = function(arg) {
146                         try {
147                                 var value = fulfillCB.apply(promise, [arg]);
148                                 resolver.resolve(value, true);
149                         }
150                         catch (e) {
151                                 resolver.reject(cloudeebus.getError(e), true);
152                         }
153                 };
154         else
155                 fulfillWrapper = function(arg) {
156                         resolver.fulfill(arg, true);
157                 };
158         
159         if (rejectCB)
160                 rejectWrapper = function(arg) {
161                         try {
162                                 var value = rejectCB.apply(promise, [arg]);
163                                 resolver.resolve(value, true);
164                         }
165                         catch (e) {
166                                 resolver.reject(cloudeebus.getError(e), true);
167                         }
168                 };
169         else
170                 rejectWrapper = function(arg) {
171                         resolver.reject(arg, true);
172                 };
173         
174         this.appendWrappers(fulfillWrapper,rejectWrapper);
175         return promise;
176 };
177
178
179 cloudeebus.Promise.prototype["catch"] = function(rejectCB) {
180         return this.then(undefined,rejectCB);
181 };
182
183
184 cloudeebus.Promise.prototype.done = function(fulfillCB, rejectCB) {
185         this.appendWrappers(fulfillCB,rejectCB);
186 };
187
188
189 cloudeebus.Promise.resolve = function(value) {
190         var promise = new cloudeebus.Promise();
191         promise.resolver.resolve(value);
192         return promise;
193 };
194
195
196 cloudeebus.Promise.fulfill = function(value) {
197         var promise = new cloudeebus.Promise();
198         promise.resolver.fulfill(value);
199         return promise;
200 };
201
202
203 cloudeebus.Promise.reject = function(value) {
204         var promise = new cloudeebus.Promise();
205         promise.resolver.reject(value);
206         return promise;
207 };
208
209
210 cloudeebus.Promise.any = function() {
211         var promise = new cloudeebus.Promise();
212         var resolver = promise.resolver;
213         var fulfillCallback = function(arg) {
214                 resolver.resolve(arg, true);
215         };
216         var rejectCallback = function(arg) {
217                 resolver.reject(arg, true);
218         };
219         if (arguments.length == 0)
220                 resolver.resolve(undefined, true);
221         else
222                 for (i in arguments) 
223                         cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
224         return promise;
225 };
226
227
228 cloudeebus.Promise.every = function() {
229         var promise = new cloudeebus.Promise();
230         var resolver = promise.resolver;
231         var index = 0;
232         var countdown = arguments.length;
233         var args = new Array(countdown);
234         var rejectCallback = function(arg) {
235                 resolver.reject(arg, true);
236         };
237         if (arguments.length == 0)
238                 resolver.resolve(undefined, true);
239         else
240                 for (i in arguments) {
241                         var fulfillCallback = function(arg) {
242                                 args[index] = arg;
243                                 countdown--;
244                                 if (countdown == 0)
245                                         resolver.resolve(args, true);
246                         };
247                         index++;
248                         cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
249                 }
250         
251         return promise;
252 };
253
254
255 cloudeebus.Promise.some = function() {
256         var promise = new cloudeebus.Promise();
257         var resolver = promise.resolver;
258         var index = 0;
259         var countdown = arguments.length;
260         var args = new Array(countdown);
261         var fulfillCallback = function(arg) {
262                 resolver.resolve(arg, true);
263         };
264         if (arguments.length == 0)
265                 resolver.resolve(undefined, true);
266         else
267                 for (i in arguments) {
268                         var rejectCallback = function(arg) {
269                                 args[index] = arg;
270                                 countdown--;
271                                 if (countdown == 0)
272                                         resolver.reject(args, true);
273                         };
274                         index++;
275                         cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
276                 }
277         
278         return promise;
279 };
280
281
282