Skip to content

Commit 2ad576d

Browse files
committed
Implement #flat_map for Promise
1 parent c147fb0 commit 2ad576d

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

lib/concurrent/promise.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,30 @@ def rescue(&block)
110110
alias_method :catch, :rescue
111111
alias_method :on_error, :rescue
112112

113+
# Yield the successful result to the block that returns a promise. If that
114+
# promise is also successful the result is the result of the yielded promise.
115+
# If either part fails the whole also fails.
116+
#
117+
# @example
118+
# Promise.execute { 1 }.flat_map { |v| Promise.execute { v + 2 } }.value! #=> 3
119+
#
120+
# @return [Promise]
121+
def flat_map(&block)
122+
child = Promise.new(
123+
parent: self
124+
)
125+
126+
on_error { |e| child.on_reject(e) }
127+
on_success do |result1|
128+
inner = block.call(result1)
129+
inner.execute
130+
inner.on_success { |result2| child.on_fulfill(result2) }
131+
inner.on_error { |e| child.on_reject(e) }
132+
end
133+
134+
child
135+
end
136+
113137
protected
114138

115139
def set_pending

spec/concurrent/promise_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,37 @@ module Concurrent
260260
end
261261
end
262262

263+
describe '#flat_map' do
264+
265+
it 'returns a promise' do
266+
child = empty_root.flat_map { nil }
267+
expect(child).to be_a Promise
268+
expect(child).not_to be empty_root
269+
end
270+
271+
it 'succeeds if both promises succeed' do
272+
child = Promise.new(executor: executor) { 1 }.
273+
flat_map { |v| Promise.new(executor: executor) { v + 10 } }.execute
274+
sleep 0.1
275+
expect(child.value!).to eq(11)
276+
end
277+
278+
it 'fails if the left promise fails' do
279+
child = Promise.new(executor: executor) { fail }.
280+
flat_map { |v| Promise.new(executor: executor) { v + 10 } }.execute
281+
sleep 0.1
282+
expect(child).to be_rejected
283+
end
284+
285+
it 'fails if the right promise fails' do
286+
child = Promise.new(executor: executor) { 1 }.
287+
flat_map { |v| Promise.new(executor: executor) { fail } }.execute
288+
sleep 0.1
289+
expect(child).to be_rejected
290+
end
291+
292+
end
293+
263294
context 'fulfillment' do
264295

265296
it 'passes the result of each block to all its children' do

0 commit comments

Comments
 (0)