-
Notifications
You must be signed in to change notification settings - Fork 5
/
promise.cljs
133 lines (122 loc) · 4.24 KB
/
promise.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
(ns redlobster.promise
(:require-macros [cljs.node-macros :as n])
(:require [redlobster.events :as e]))
(defprotocol IPromise
(realised? [this])
(failed? [this])
(realise [this value])
(realise-error [this value])
(on-realised [this on-success on-error]))
(defn promise? [v]
(satisfies? IPromise v))
(deftype Promise [ee]
IDeref
(-deref [this]
(let [realised (.-__realised ee)
value (.-__value ee)]
(cond
(not realised) :redlobster.promise/not-realised
:else value)))
IPromise
(realised? [this]
(if (nil? (.-__realised ee)) false true))
(failed? [this]
(and (realised? this) (= :error (.-__realised ee))))
(realise [this value]
(if (realised? this)
(when-not (= :redlobster.promise/timeout @this)
(throw :redlobster.promise/already-realised))
(if (promise? value)
(on-realised value
#(realise this %)
#(realise-error this %))
(do
(set! (.-__realised ee) :success)
(set! (.-__value ee) value)
(e/emit ee :realise-success value)))))
(realise-error [this value]
(if (realised? this)
(when-not (= :redlobster.promise/timeout @this)
(throw :redlobster.promise/already-realised))
(if (promise? value)
(on-realised value
#(realise this %)
#(realise-error this %))
(do
(set! (.-__realised ee) :error)
(set! (.-__value ee) value)
(e/emit ee :realise-error value)))))
(on-realised [this on-success on-error]
(if (realised? this)
(if (failed? this) (on-error @this) (on-success @this))
(doto ee
(e/on :realise-success on-success)
(e/on :realise-error on-error)))))
(defn promise
([]
(Promise.
(let [ee (e/event-emitter)]
(set! (.-__realised ee) nil)
(set! (.-__value ee) nil)
ee)))
([success-value]
(doto (promise)
(realise success-value))))
(defn promise-fail [error-value]
(doto (promise)
(realise-error error-value)))
(defn await
"Takes a list of promises, and creates a promise that will realise as
`:redlobster.promise/realised` when each promise has successfully realised,
or if one or more of the promises fail, fail with the value of the first
failing promise.
If the first argument is the keyword `:all`, then instead of failing when
one of the promises fails, it will just wait for all promises to realise
and realise itself with `:redlobster.promise/realised` regardless of the
success or failure of any promise."
[& promises]
(let [await-all (= (first promises) :all)
promises (if await-all (rest promises) promises)
p (promise)
total (count promises)
count (atom 0)
done (atom false)]
(doseq [subp promises]
(let [succ (fn [_]
(when (not @done)
(swap! count inc)
(when (= total @count)
(reset! done true)
(realise p :redlobster.promise/realised))))
fail (if await-all succ
(fn [err]
(when (not @done)
(reset! done true)
(realise-error p err))))]
(on-realised subp succ fail)))
p))
(defn defer-until-realised [promises callback]
(let [p (promise)]
(on-realised (apply await promises)
(fn [_] (realise p (callback)))
(fn [error] (realise-error p error)))
p))
(defn on-event
"Creates a promise that fulfills with an event object when the matching
event is triggered on the EventEmitter. This promise cannot fail; it will
either succeed or never realise."
[ee type]
(let [p (promise)]
(e/once ee type
(fn [event] (realise p event)))
p))
(defn timeout
"Sets a promise to fail with `:redlobster.promise/timeout` after a
specified number of milliseconds.
A promise that has timed out will not throw an error when you try to
realise it, but the realised value will remain
`:redlobster.promise/timeout`."
[promise timeout]
(let [timeout-func #(when-not (realised? promise)
(realise-error promise :redlobster.promise/timeout))]
(js/setTimeout timeout-func timeout)))