1 | | /// cassie.js --- Simple future library for JS. Ready to be raped by Ajax! |
2 | | // |
3 | | // // Copyright (c) 2011 Quildreen Motta |
4 | | // |
5 | | // Permission is hereby granted, free of charge, to any person |
6 | | // obtaining a copy of this software and associated documentation files |
7 | | // (the "Software"), to deal in the Software without restriction, |
8 | | // including without limitation the rights to use, copy, modify, merge, |
9 | | // publish, distribute, sublicense, and/or sell copies of the Software, |
10 | | // and to permit persons to whom the Software is furnished to do so, |
11 | | // subject to the following conditions: |
12 | | // |
13 | | // The above copyright notice and this permission notice shall be |
14 | | // included in all copies or substantial portions of the Software. |
15 | | // |
16 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
17 | | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
18 | | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
19 | | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
20 | | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
21 | | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
22 | | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
23 | | |
24 | | /// Module cassie |
25 | | |
26 | | |
27 | | //// -- Dependencies -------------------------------------------------------- |
28 | 1 | var Base = require('boo').Base |
29 | | |
30 | | |
31 | | |
32 | | //// -- Aliases ------------------------------------------------------------- |
33 | 1 | var slice = [].slice |
34 | | |
35 | | |
36 | | |
37 | | //// -- Helpers ------------------------------------------------------------- |
38 | | |
39 | | ///// Function get_queue |
40 | | // Returns a list of callbacks registered for the event. |
41 | | // |
42 | | // If callbacks ain't defined for the event yet, it's also *initialised* |
43 | | // to an empty array. |
44 | | // |
45 | | // get_queue! :: Promise*, String -> [Fun] |
46 | 1 | function get_queue(promise, event) { |
47 | 60 | return promise.callbacks[event] |
48 | | || (promise.callbacks[event] = []) } |
49 | | |
50 | | |
51 | | ///// Function register |
52 | | // Creates a function that registers handlers for the given event. |
53 | | // |
54 | | // register! :: String -> @this:Promise*, Fun -> this |
55 | 7 | function register(event) { return function(fun) { |
56 | 12 | return this.on(event, fun) }} |
57 | | |
58 | | |
59 | | |
60 | | //// -- Public interface ---------------------------------------------------- |
61 | | |
62 | | ///// Object Promise <| Base |
63 | | // A placeholder for a value that can be computed asynchronously. |
64 | | // |
65 | | // The `Promise' allows any code to define how they'll handle the value |
66 | | // before the value is actually computed, by adding listeners to the |
67 | | // various events that can be triggered once a promise is fulfilled. |
68 | | // |
69 | | // Promise :: { "callbacks" -> { String -> [Fun] } |
70 | | // , "flush_queue" -> [Fun] |
71 | | // , "value" -> [Any] |
72 | | // , "timer" -> TimerID |
73 | | // , "default_event" -> String |
74 | | // } |
75 | 1 | var Promise = Base.derive({ |
76 | | ///// Function init |
77 | | // Initialises an instance of a Promise. |
78 | | // |
79 | | // init! :: @this:Object* -> this |
80 | | init: |
81 | | function _init() { |
82 | 21 | this.callbacks = {} |
83 | 21 | this.flush_queue = [] |
84 | 21 | this.value = null |
85 | 21 | this.timer = null |
86 | 21 | this.default_event = 'done' |
87 | 21 | return this } |
88 | | |
89 | | |
90 | | ///// Function on |
91 | | // Adds a callback to the given event. |
92 | | // |
93 | | // on! :: @this:Promise*, String, Fun -> this |
94 | | , on: |
95 | | function _on(event, callback) { |
96 | 27 | this.default_event = event |
97 | | |
98 | 28 | if (this.value) invoke_callback(this) |
99 | 26 | else add_callback(this) |
100 | | |
101 | 27 | return this |
102 | | |
103 | | // Invokes all the callbacks for the event |
104 | 0 | function invoke_callback(promise) { |
105 | 1 | var queue = get_queue(promise, event) |
106 | 1 | return callback && queue.flushed? callback.apply(promise, promise.value) |
107 | | : /* otherwise */ null } |
108 | | |
109 | | // Adds the callback to the event |
110 | 0 | function add_callback(promise) { |
111 | 26 | return callback? get_queue(promise, event).push(callback) |
112 | | : null }} |
113 | | |
114 | | |
115 | | ///// Function then |
116 | | // Adds a callback to the active event queue. |
117 | | // |
118 | | // The active event queue is the one for which the last callback was |
119 | | // registered, usually. It is controlled by the internal |
120 | | // `default_event' property. |
121 | | // |
122 | | // then! :: @this:Promise*, Fun -> this |
123 | | , then: |
124 | | function _then(callback) { |
125 | 3 | return this.on(this.default_event, callback) } |
126 | | |
127 | | |
128 | | |
129 | | ///// Function flush |
130 | | // Fires all the callbacks for the event. |
131 | | // |
132 | | // If the promise hasn't been resolved yet, the callbacks are placed |
133 | | // in a queue to be flushed once the Promise is fulfilled. |
134 | | // |
135 | | // flush :: @this:Promise*, String -> this |
136 | | , flush: |
137 | | function _flush(event) { |
138 | 47 | var self = this |
139 | | |
140 | 47 | !this.value? queue_event(event) |
141 | | : event? flush_queue(event) |
142 | | : /* otherwise */ flush_all() |
143 | | |
144 | 47 | return this |
145 | | |
146 | | |
147 | | // Adds the event to the flush queue |
148 | 0 | function queue_event(event) { |
149 | 60 | if (event) self.flush_queue.push(event) } |
150 | | |
151 | | // Calls all of the callbacks related to a given event |
152 | 0 | function flush_queue(event) { |
153 | 33 | var callbacks = get_queue(self, event) |
154 | | |
155 | 33 | callbacks.forEach(function(callback) { |
156 | 14 | callback.apply(self, self.value) }) |
157 | 33 | callbacks.length = 0 |
158 | 33 | callbacks.flushed = true } |
159 | | |
160 | | // Calls the callbacks for all events that have been queued |
161 | 0 | function flush_all() { |
162 | 15 | self.flush_queue.forEach(flush_queue) }} |
163 | | |
164 | | |
165 | | ///// Function done |
166 | | // Fulfills the promise with the values given. |
167 | | // |
168 | | // done :: @this:Promise*, [Any] -> this |
169 | | , done: |
170 | | function _done(values) { |
171 | 14 | if (!this.value) { |
172 | 13 | this.clear_timer() |
173 | 13 | this.flush('done') |
174 | 13 | this.value = slice.call(values) |
175 | 13 | this.flush() } |
176 | | |
177 | 14 | return this } |
178 | | |
179 | | |
180 | | ///// Function fail |
181 | | // Fails to fulfill the promise. |
182 | | // |
183 | | // fail :: @this:Promise*, Any... -> this |
184 | | , fail: |
185 | | function _fail() { |
186 | 5 | return this.flush('failed').done(arguments) } |
187 | | |
188 | | |
189 | | ///// Function bind |
190 | | // Successfully fulfills the promise. |
191 | | // |
192 | | // bind :: @this:Promise*, Any... -> this |
193 | | , bind: |
194 | | function _bind() { |
195 | 5 | return this.flush('ok').done(arguments) } |
196 | | |
197 | | |
198 | | ///// Function forget |
199 | | // Cancels the promise. |
200 | | // |
201 | | // forget :: @this:Promise* -> this |
202 | | , forget: |
203 | | function _forget() { |
204 | 1 | return this.flush('forgotten').fail('forgotten') } |
205 | | |
206 | | |
207 | | ///// Function timeout |
208 | | // Schedules the promise to fail after a given number of seconds. |
209 | | // |
210 | | // timeout :: @this:Promise*, Number -> this |
211 | | , timeout: |
212 | | function _timeout(delay) { |
213 | 4 | this.clear_timer() |
214 | 6 | this.timer = setTimeout( function(){ this.flush('timeouted') |
215 | | .fail('timeouted') }.bind(this) |
216 | | , delay * 1000) |
217 | | |
218 | 4 | return this } |
219 | | |
220 | | |
221 | | ///// Function clear_timer |
222 | | // Stop the timer for the promise, if one was previously set by |
223 | | // invoking `timeout'. |
224 | | // |
225 | | // clear_timer :: @this:Promise* -> this |
226 | | , clear_timer: |
227 | | function _clear_timer() { |
228 | 18 | clearTimeout(this.timer) |
229 | 18 | this.timer = null |
230 | 18 | return this } |
231 | | |
232 | | |
233 | | ///// Function ok |
234 | | // Registers a callback for when the promise is successfully |
235 | | // fulfilled. |
236 | | // |
237 | | // ok :: @this:Promise*, Fun -> this |
238 | | , ok: register('ok') |
239 | | |
240 | | ///// Function failed |
241 | | // Registers a callback for when the promise fails to be fulfilled. |
242 | | // |
243 | | // failed :: @this:Promise*, Fun -> this |
244 | | , failed: register('failed') |
245 | | |
246 | | ///// Function timeouted |
247 | | // Registers a callback for when the promise fails by timing out. |
248 | | // |
249 | | // timeouted :: @this:Promise*, Fun -> this |
250 | | , timeouted: register('timeouted') |
251 | | |
252 | | ///// Function forgotten |
253 | | // Registers a callback for when the promise fails by being |
254 | | // cancelled. |
255 | | // |
256 | | // forgotten :: @this:Promise*, Fun -> this |
257 | | , forgotten: register('forgotten') |
258 | | }) |
259 | | |
260 | | |
261 | | |
262 | | //// -- Exports --------------------------------------------------------------- |
263 | 1 | module.exports = { Promise : Promise |
264 | | , register : register |
265 | | |
266 | | , internals : { get_queue: get_queue }} |