Skip to content

Commit 1da0bf4

Browse files
committed
WIP
1 parent 3bf739f commit 1da0bf4

File tree

5 files changed

+340
-0
lines changed

5 files changed

+340
-0
lines changed
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require 'promise'
2+
3+
describe 'Promise.error' do
4+
it 'rejects the promise with the given error' do
5+
Promise.error(23).error.should == 23
6+
end
7+
8+
it 'marks the promise as realized' do
9+
Promise.error(23).realized?.should be_true
10+
end
11+
12+
it 'marks the promise as rejected' do
13+
Promise.error(23).rejected?.should be_true
14+
end
15+
end
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require 'promise'
2+
3+
describe 'Promise#rescue' do
4+
it 'calls the block when the promise has already been rejected' do
5+
x = 42
6+
Promise.error(23).rescue { |v| x = v }
7+
x.should == 23
8+
end
9+
10+
it 'calls the block when the promise is rejected' do
11+
a = Promise.new
12+
x = 42
13+
14+
a.rescue { |v| x = v }
15+
a.reject(23)
16+
17+
x.should == 23
18+
end
19+
20+
it 'does not call then blocks when the promise is rejected' do
21+
x = 42
22+
y = 23
23+
24+
Promise.error(23).then { y = 42 }.rescue { |v| x = v }
25+
26+
x.should == 23
27+
y.should == 23
28+
end
29+
30+
it 'does not call subsequent rescue blocks' do
31+
x = 42
32+
Promise.error(23).rescue { |v| x = v }.rescue { x = 42 }
33+
x.should == 23
34+
end
35+
end

spec/opal/stdlib/promise/then_spec.rb

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require 'promise'
2+
3+
describe 'Promise#then' do
4+
it 'calls the block when the promise has already been resolved' do
5+
x = 42
6+
Promise.value(23).then { |v| x = v }
7+
x.should == 23
8+
end
9+
10+
it 'calls the block when the promise is resolved' do
11+
a = Promise.new
12+
x = 42
13+
14+
a.then { |v| x = v }
15+
a.resolve(23)
16+
17+
x.should == 23
18+
end
19+
20+
it 'works with multiple chains' do
21+
x = 42
22+
Promise.value(2).then { |v| v * 2 }.then { |v| v * 4 }.then { |v| x = v }
23+
x.should == 16
24+
end
25+
26+
it 'works when a block returns a promise' do
27+
a = Promise.new
28+
b = Promise.new
29+
30+
x = 42
31+
a.then { b }.then { |v| x = v }
32+
33+
a.resolve(42)
34+
b.resolve(23)
35+
36+
x.should == 23
37+
end
38+
end
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require 'promise'
2+
3+
describe 'Promise.value' do
4+
it 'resolves the promise with the given value' do
5+
Promise.value(23).value.should == 23
6+
end
7+
8+
it 'marks the promise as realized' do
9+
Promise.value(23).realized?.should be_true
10+
end
11+
12+
it 'marks the promise as resolved' do
13+
Promise.value(23).resolved?.should be_true
14+
end
15+
end

stdlib/promise.rb

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
class Promise
2+
def self.value(value)
3+
new.resolve(value)
4+
end
5+
6+
def self.error(value)
7+
new.reject(value)
8+
end
9+
10+
def self.when(*promises)
11+
When.new(promises.flatten)
12+
end
13+
14+
attr_reader :value, :error, :prev, :next
15+
16+
def initialize(success = nil, failure = nil)
17+
@success = success
18+
@failure = failure
19+
20+
@realized = nil
21+
@value = nil
22+
@error = nil
23+
@delayed = nil
24+
25+
@prev = nil
26+
@next = nil
27+
end
28+
29+
def act?
30+
`#@success !== nil`
31+
end
32+
33+
def realized?
34+
`#@realized !== nil`
35+
end
36+
37+
def resolved?
38+
`#@realized === "value"`
39+
end
40+
41+
def rejected?
42+
`#@realized === "error"`
43+
end
44+
45+
def ^(promise)
46+
self >> promise
47+
promise << self
48+
end
49+
50+
def <<(promise)
51+
@prev = promise
52+
53+
self
54+
end
55+
56+
def >>(promise)
57+
@next = promise
58+
59+
if resolved?
60+
promise.resolve(@delayed || value)
61+
elsif rejected? && (!@failure || Promise === (@delayed || @error))
62+
promise.reject(@delayed || error)
63+
end
64+
65+
self
66+
end
67+
68+
def resolve(value)
69+
if realized?
70+
raise ArgumentError, 'the promise has already been realized'
71+
end
72+
73+
if Promise === value
74+
return value ^ self
75+
end
76+
77+
@realized = :value
78+
@value = value
79+
80+
if @success
81+
value = @success.call(value)
82+
end
83+
84+
if @next
85+
@next.resolve(value)
86+
else
87+
@delayed = value
88+
end
89+
90+
self
91+
end
92+
93+
def reject(value)
94+
if realized?
95+
raise ArgumentError, 'the promise has already been realized'
96+
end
97+
98+
if Promise === value
99+
return value ^ self
100+
end
101+
102+
@realized = :error
103+
@error = value
104+
105+
if @failure
106+
value = @failure.call(value)
107+
108+
if Promise === value
109+
if @next
110+
@next.reject(value)
111+
else
112+
@delayed = value
113+
end
114+
end
115+
else
116+
if @next
117+
@next.reject(value)
118+
else
119+
@delayed = value
120+
end
121+
end
122+
123+
self
124+
end
125+
126+
def then(&block)
127+
self ^ Promise.new(block)
128+
end
129+
130+
alias do then
131+
132+
def fail(&block)
133+
self ^ Promise.new(nil, block)
134+
end
135+
136+
alias rescue fail
137+
alias catch fail
138+
139+
def always(&block)
140+
self ^ Promise.new(block, block)
141+
end
142+
143+
alias finally always
144+
alias ensure always
145+
146+
def collect(&block)
147+
self ^ Collect.new(block)
148+
end
149+
150+
def inspect
151+
result = "#<Promise(#{object_id})"
152+
153+
if @next
154+
result += " >> #{@next.inspect}"
155+
end
156+
157+
if realized?
158+
result += ": #{(@value || @error).inspect}>"
159+
else
160+
result += ">"
161+
end
162+
163+
result
164+
end
165+
166+
class Collect < self
167+
def self.it(promise)
168+
unless promise.realized?
169+
raise ArgumentError, "the promise hasn't been realized"
170+
end
171+
172+
current = promise.act? ? [promise.value] : []
173+
174+
if prev = promise.prev
175+
current.concat(it(prev))
176+
else
177+
current
178+
end
179+
end
180+
181+
def initialize(block)
182+
super()
183+
184+
@block = block
185+
end
186+
187+
def <<(promise)
188+
super
189+
190+
self.then {|value|
191+
@block.call(*(Collect.it(self).reverse + [value]))
192+
}
193+
end
194+
end
195+
196+
class When < self
197+
def initialize(promises = [])
198+
super()
199+
200+
@wait = []
201+
202+
promises.each {|promise|
203+
wait promise
204+
}
205+
end
206+
207+
def wait(promise)
208+
@wait << promise
209+
210+
promise.always {
211+
try if @next
212+
}
213+
214+
self
215+
end
216+
217+
def >>(*)
218+
super.tap {
219+
try
220+
}
221+
end
222+
223+
def try
224+
if @wait.all?(&:realized?)
225+
if promise = @wait.find(&:rejected?)
226+
reject(promise.error)
227+
else
228+
resolve(@wait.map(&:value))
229+
end
230+
end
231+
end
232+
233+
def and(&block)
234+
wait Promise.new(block)
235+
end
236+
end
237+
end

0 commit comments

Comments
 (0)