Coverage

90%
52
47
5

cassie.js

90%
52
47
5
LineHitsSource
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 --------------------------------------------------------
281var Base = require('boo').Base
29
30
31
32//// -- Aliases -------------------------------------------------------------
331var 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]
461function get_queue(promise, event) {
4760 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
557function register(event) { return function(fun) {
5612 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// }
751var Promise = Base.derive({
76 ///// Function init
77 // Initialises an instance of a Promise.
78 //
79 // init! :: @this:Object* -> this
80 init:
81 function _init() {
8221 this.callbacks = {}
8321 this.flush_queue = []
8421 this.value = null
8521 this.timer = null
8621 this.default_event = 'done'
8721 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) {
9627 this.default_event = event
97
9828 if (this.value) invoke_callback(this)
9926 else add_callback(this)
100
10127 return this
102
103 // Invokes all the callbacks for the event
1040 function invoke_callback(promise) {
1051 var queue = get_queue(promise, event)
1061 return callback && queue.flushed? callback.apply(promise, promise.value)
107 : /* otherwise */ null }
108
109 // Adds the callback to the event
1100 function add_callback(promise) {
11126 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) {
1253 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) {
13847 var self = this
139
14047 !this.value? queue_event(event)
141 : event? flush_queue(event)
142 : /* otherwise */ flush_all()
143
14447 return this
145
146
147 // Adds the event to the flush queue
1480 function queue_event(event) {
14960 if (event) self.flush_queue.push(event) }
150
151 // Calls all of the callbacks related to a given event
1520 function flush_queue(event) {
15333 var callbacks = get_queue(self, event)
154
15533 callbacks.forEach(function(callback) {
15614 callback.apply(self, self.value) })
15733 callbacks.length = 0
15833 callbacks.flushed = true }
159
160 // Calls the callbacks for all events that have been queued
1610 function flush_all() {
16215 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) {
17114 if (!this.value) {
17213 this.clear_timer()
17313 this.flush('done')
17413 this.value = slice.call(values)
17513 this.flush() }
176
17714 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() {
1865 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() {
1955 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() {
2041 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) {
2134 this.clear_timer()
2146 this.timer = setTimeout( function(){ this.flush('timeouted')
215 .fail('timeouted') }.bind(this)
216 , delay * 1000)
217
2184 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() {
22818 clearTimeout(this.timer)
22918 this.timer = null
23018 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 ---------------------------------------------------------------
2631module.exports = { Promise : Promise
264 , register : register
265
266 , internals : { get_queue: get_queue }}