Module: Ione::Future::Factories

Included in:
Ione::Future
Defined in:
lib/ione/future.rb

Overview

Since:

  • v1.0.0

Instance Method Summary collapse

Instance Method Details

#after(*futures) ⇒ Ione::Future

Combines multiple futures into a new future which resolves when all constituent futures complete, or fails when one or more of them fails.

The resulting future has no value.

Examples:

future = Future.after(delete_thing(1), delete_thing(2))
future.value # => nil

Parameters:

  • futures (Array<Ione::Future>)

    the futures to combine (this argument can be a splatted array or a regular array passed as sole argument)

Returns:

  • (Ione::Future)

    with a nil value once all futures have succeeded

Since:

  • v1.2.1



232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/ione/future.rb', line 232

def after(*futures)
  if futures.size == 1 && (fs = futures.first).is_a?(Enumerable)
    *futures = *fs
  end
  futures.reject! { |f| f.respond_to?(:resolved?) && f.resolved? }
  if futures.count == 0
    ResolvedFuture::NIL
  elsif futures.count == 1
    futures.first.map(nil)
  else
    CombinedNilFuture.new(futures)
  end
end

#all(*futures) ⇒ Ione::Future<Array>

Combines multiple futures into a new future which resolves when all constituent futures complete, or fails when one or more of them fails.

The value of the combined future is an array of the values of the constituent futures.

Examples:

ids = [1, 2, 3, 4]
futures = ids.map { |id| find_thing(id) }
future = Future.all(ids)
future.value # => [thing1, thing2, thing3, thing4]

Parameters:

  • futures (Array<Ione::Future>)

    the futures to combine (this argument can be a splatted array or a regular array passed as sole argument)

Returns:

  • (Ione::Future<Array>)

    an array of the values of the constituent futures

Since:

  • v1.0.0



206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/ione/future.rb', line 206

def all(*futures)
  if futures.size == 1 && (fs = futures.first).is_a?(Enumerable)
    futures = fs
  end
  if futures.count == 0
    resolved([])
  elsif (failed = futures.find { |f| f.respond_to?(:failed?) && f.failed? })
    failed
  else
    CombinedFuture.new(futures)
  end
end

#failed(error) ⇒ Ione::Future

Creates a new pre-failed future.

Parameters:

  • error (Error)

    the error of the created future

Returns:

Since:

  • v1.0.0



368
369
370
# File 'lib/ione/future.rb', line 368

def failed(error)
  FailedFuture.new(error)
end

#first(*futures) ⇒ Ione::Future

Returns a future which will be resolved with the value of the first (resolved) of the specified futures. If all of the futures fail, the returned future will also fail (with the error of the last failed future).

Examples:

Speculative execution

# make a call to multiple services and use the value of the one that
# responds first – and discard the other results
f1 = service1.find_thing(id)
f2 = service2.find_thing(id)
f3 = service3.find_thing(id)
f = Future.first(f1, f2, f3)
f.value # => the value of the call that was quickest

Parameters:

  • futures (Array<Ione::Future>)

    the futures to monitor (this argument can be a splatted array or a regular array passed as sole argument)

Returns:

  • (Ione::Future)

    a future which represents the first completing future

Since:

  • v1.0.0



263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/ione/future.rb', line 263

def first(*futures)
  if futures.size == 1 && (fs = futures.first).is_a?(Enumerable)
    futures = fs
  end
  if futures.count == 0
    ResolvedFuture::NIL
  elsif (done = futures.find { |f| f.respond_to?(:resolved?) && f.resolved? })
    done
  else
    FirstFuture.new(futures)
  end
end

#reduce(futures, initial_value = nil, options = nil) {|accumulator, value| ... } ⇒ Ione::Future

Returns a future that will resolve to a value which is the reduction of the values of a list of source futures.

This is essentially a parallel, streaming version of Enumerable#reduce, but for futures. Use this method for example when you want to do a number of asynchronous operations in parallel and then merge the results together when all are done.

The block will not be called concurrently, which means that unless you're handling the initial value or other values in the scope of the block you don't need (and shouldn't) do any locking to ensure that the accumulator passed to the block is safe to modify. It is, of course, even better if you don't modify the accumulator, but return a new, immutable value on each invocation.

Examples:

Merging the results of multipe asynchronous calls

futures = ... # a list of futures that will resolve to hashes
merged_future = Future.reduce(futures, {}) do |accumulator, value|
  accumulator.merge(value)
end
merged_future.value # => the result of {}.merge(hash1).merge(hash2), etc.

Reducing with an associative and commutative function, like addition

futures = ... # a list of futures that will resolve to numbers
sum_future = Future.reduce(futures, 0, ordered: false) do |accumulator, value|
  accumulator + value
end
sum_future.value # => the sum of all values

Parameters:

  • futures (Array<Ione::Future>)

    an array of futures whose values should be reduced

  • initial_value (Object) (defaults to: nil)

    the initial value of the accumulator. When nil (the default) the value of the first future will be used instead.

  • options (Hash) (defaults to: nil)

Options Hash (options):

  • :ordered (Boolean) — default: true

    whether or not to respect the order of the input when reducing – when true the block will be called with the values of the source futures in the order they have in the given list, when false the block will be called in the order that the futures resolve (which means that your reducer function needs to be associative and commutative).

Yield Parameters:

  • accumulator (Object)

    the value of the last invocation of the block, or the initial value if this is the first invocation

  • value (Object)

    the value of one of the source futures

Yield Returns:

  • (Object)

    the value to pass as accumulator to the next invocation of the block

Returns:

  • (Ione::Future)

    a future that will resolve to the value returned from the last invocation of the block, or nil when the list of futures is empty.

Since:

  • v1.2.0



347
348
349
350
351
352
353
# File 'lib/ione/future.rb', line 347

def reduce(futures, initial_value=nil, options=nil, &reducer)
  if options && options[:ordered] == false
    UnorderedReducingFuture.new(futures, initial_value, reducer)
  else
    OrderedReducingFuture.new(futures, initial_value, reducer)
  end
end

#resolved(value = nil) ⇒ Ione::Future

Creates a new pre-resolved future.

Parameters:

  • value (Object, nil) (defaults to: nil)

    the value of the created future

Returns:

Since:

  • v1.0.0



359
360
361
362
# File 'lib/ione/future.rb', line 359

def resolved(value=nil)
  return ResolvedFuture::NIL if value.nil?
  ResolvedFuture.new(value)
end

#traverse(values) {|value| ... } ⇒ Ione::Future

Takes calls the block once for each element in an array, expecting each invocation to return a future, and returns a future that resolves to an array of the values of those futures.

Examples:

ids = [1, 2, 3]
future = Future.traverse(ids) { |id| find_thing(id) }
future.value # => [thing1, thing2, thing3]

Parameters:

  • values (Array<Object>)

    an array whose elements will be passed to the block, one by one

Yield Parameters:

  • value (Object)

    each element from the array

Yield Returns:

Returns:

  • (Ione::Future)

    a future that will resolve to an array of the values of the futures returned by the block

Since:

  • v1.2.0



292
293
294
295
296
# File 'lib/ione/future.rb', line 292

def traverse(values, &block)
  all(values.map(&block))
rescue => e
  failed(e)
end