Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.zip execution changes #654

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 23 additions & 3 deletions lib/concurrent/promise.rb
Expand Up @@ -383,11 +383,24 @@ def flat_map(&block)
# Builds a promise that produces the result of promises in an Array
# and fails if any of them fails.
#
# @param [Array<Promise>] promises
# @overload zip(*promises)
# @param [Array<Promise>] promises
#
# @overload zip(*promises, opts)
# @param [Array<Promise>] promises
# @param [Hash] opts the configuration options
# @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
# @option opts [Boolean] :execute (true) execute promise before returning
#
# @return [Promise<Array>]
def self.zip(*promises)
zero = fulfill([], executor: ImmediateExecutor.new)
opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {}
opts[:executor] ||= ImmediateExecutor.new
zero = if !opts.key?(:execute) || opts.delete(:execute)
fulfill([], opts)
else
Promise.new(opts) { [] }
end

promises.reduce(zero) do |p1, p2|
p1.flat_map do |results|
Expand All @@ -401,7 +414,14 @@ def self.zip(*promises)
# Builds a promise that produces the result of self and others in an Array
# and fails if any of them fails.
#
# @param [Array<Promise>] others
# @overload zip(*promises)
# @param [Array<Promise>] others
#
# @overload zip(*promises, opts)
# @param [Array<Promise>] others
# @param [Hash] opts the configuration options
# @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
# @option opts [Boolean] :execute (true) execute promise before returning
#
# @return [Promise<Array>]
def zip(*others)
Expand Down
52 changes: 52 additions & 0 deletions spec/concurrent/promise_spec.rb
Expand Up @@ -357,6 +357,32 @@ def get_ivar_from_args(opts)
let(:promise2) { Promise.new(executor: :immediate) { 2 } }
let(:promise3) { Promise.new(executor: :immediate) { [3] } }

it 'executes the returned Promise by default' do
composite = promise1.zip(promise2, promise3)

expect(composite).to be_fulfilled
end

it 'executes the returned Promise when execute is true' do
composite = promise1.zip(promise2, promise3, execute: true)

expect(composite).to be_fulfilled
end

it 'does not execute the returned Promise when execute is false' do
composite = promise1.zip(promise2, promise3, execute: false)

expect(composite).to be_unscheduled
end

it 'allows setting executor for Promise chain' do
new_executor = Concurrent::SingleThreadExecutor.new
promise = promise1.zip(promise2, promise3, executor: new_executor)

promise = promise.instance_variable_get(:@parent) until promise.send(:root?)
expect(promise.instance_variable_get(:@executor)).to be(new_executor)
end

it 'yields the results as an array' do
composite = promise1.zip(promise2, promise3).execute.wait

Expand All @@ -375,6 +401,32 @@ def get_ivar_from_args(opts)
let(:promise2) { Promise.new(executor: :immediate) { 2 } }
let(:promise3) { Promise.new(executor: :immediate) { [3] } }

it 'executes the returned Promise by default' do
composite = Promise.zip(promise1, promise2, promise3)

expect(composite).to be_fulfilled
end

it 'executes the returned Promise when execute is true' do
composite = Promise.zip(promise1, promise2, promise3, execute: true)

expect(composite).to be_fulfilled
end

it 'does not execute the returned Promise when execute is false' do
composite = Promise.zip(promise1, promise2, promise3, execute: false)

expect(composite).to be_unscheduled
end

it 'allows setting executor for Promise chain' do
new_executor = Concurrent::SingleThreadExecutor.new
promise = Promise.zip(promise1, promise2, promise3, executor: new_executor)

promise = promise.instance_variable_get(:@parent) until promise.send(:root?)
expect(promise.instance_variable_get(:@executor)).to be(new_executor)
end

it 'yields the results as an array' do
composite = Promise.zip(promise1, promise2, promise3).execute.wait

Expand Down