Class: Fear::Future
Overview
Asynchronous computations that yield futures are created with the Fear.future
call:
success = "Hello"
f = Fear.future { success + ' future!' }
f.on_success do |result|
puts result
end
Multiple callbacks may be registered; there is no guarantee that they will be executed in a particular order.
The future may contain an exception and this means that the future failed. Futures obtained through combinators have the same error as the future they were obtained from.
f = Fear.future { 5 }
g = Fear.future { 3 }
f.flat_map do |x|
g.map { |y| x + y }
end
The same program may be written using Fear.for
Fear.for(Fear.future { 5 }, Fear.future { 3 }) do |x, y|
x + y
end
Futures use Concurrent::Promise
under the hood. Fear.future
accepts optional configuration Hash passed directly to underlying promise. For example, run it on custom thread pool.
require 'open-uri'
future = Fear.future(executor: :io) { open('https://example.com/') }
future.map(executor: :fast, &:read).each do |body|
puts "#{body}"
end
Class Method Summary collapse
-
.failed(exception) ⇒ Fear::Future
Creates an already completed
Future
with the specified error. -
.successful(result) ⇒ Fear::Future
Creates an already completed
Future
with the specified result.
Instance Method Summary collapse
- #__ready__(at_most) ⇒ Object private
- #__result__(at_most) ⇒ Object private
-
#and_then ⇒ Object
Applies the side-effecting block to the result of
self
future, and returns a new future with the result of this future. -
#completed? ⇒ true, false
Returns whether the future has already been completed with a value or an error.
-
#fallback_to(fallback) ⇒ Fear::Future
Creates a new future which holds the result of
self
future if it was completed successfully, or, if not, the result of thefallback
future iffallback
is completed successfully. -
#flat_map {|| ... } ⇒ Fear::Future
Creates a new future by applying a block to the successful result of this future, and returns the result of the function as the new future.
-
#initialize(promise = nil, **options) { ... } ⇒ Future
constructor
private
A new instance of Future.
-
#map(&block) ⇒ Fear::Future
Creates a new future by applying a block to the successful result of this future.
-
#on_complete {|| ... } ⇒ self
When this future is completed call the provided block.
-
#on_complete_match {|| ... } ⇒ self
When this future is completed match against result.
-
#on_failure {|| ... } ⇒ self
When this future is completed with a failure apply the provided callback to the error.
-
#on_failure_match {|m| ... } ⇒ self
When this future is completed with a failure match against the error.
-
#on_success {|value| ... } ⇒ self
(also: #each)
Calls the provided callback when this future is completed successfully.
-
#on_success_match {|m| ... } ⇒ self
When this future is completed successfully match against its result.
-
#recover(&block) ⇒ Fear::Future
Creates a new future that will handle any matching error that this future might contain.
-
#select {|| ... } ⇒ Fear::Future
Creates a new future by filtering the value of the current future with a predicate.
-
#transform(success, failure) {|success, failure| ... } ⇒ Fear::Future
Creates a new future by applying the
success
function to the successful result of this future, or thefailure
function to the failed result. -
#value ⇒ Fear::Option<Fear::Try>
The value of this
Future
. -
#zip(other) ⇒ Fear::Future
Zips the values of
self
andother
future, and creates a new future holding the array of their results.
Constructor Details
#initialize(promise = nil, **options) { ... } ⇒ Future
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Future.
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/fear/future.rb', line 56 def initialize(promise = nil, **, &block) if block_given? && promise raise ArgumentError, "pass block or promise" end @options = @promise = promise || Concurrent::Promise.execute(@options) do Fear.try(&block) end end |
Class Method Details
.failed(exception) ⇒ Fear::Future
Creates an already completed Future
with the specified error.
474 475 476 477 |
# File 'lib/fear/future.rb', line 474 def failed(exception) new { raise exception } .yield_self { |future| Fear::Await.ready(future, 10) } end |
.successful(result) ⇒ Fear::Future
Creates an already completed Future
with the specified result.
483 484 485 486 |
# File 'lib/fear/future.rb', line 483 def successful(result) new { result } .yield_self { |future| Fear::Await.ready(future, 10) } end |
Instance Method Details
#__ready__(at_most) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
461 462 463 464 465 466 467 |
# File 'lib/fear/future.rb', line 461 def __ready__(at_most) if promise.wait(at_most).complete? self else raise Timeout::Error end end |
#__result__(at_most) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
456 457 458 |
# File 'lib/fear/future.rb', line 456 def __result__(at_most) __ready__(at_most).value.get_or_else { raise "promise not completed" } end |
#and_then ⇒ Object
that if one of the chained and_then
callbacks throws
Applies the side-effecting block to the result of self
future, and returns a new future with the result of this future.
This method allows one to enforce that the callbacks are executed in a specified order.
an error, that error is not propagated to the subsequent and_then
callbacks. Instead, the subsequent and_then
callbacks are given the original value of this future.
443 444 445 446 447 448 449 450 451 452 453 |
# File 'lib/fear/future.rb', line 443 def and_then promise = Promise.new(**@options) on_complete do |try| Fear.try do Fear::Try.matcher { |m| yield(m) }.call_or_else(try, &:itself) end promise.complete!(try) end promise.to_future end |
#completed? ⇒ true, false
Returns whether the future has already been completed with a value or an error.
201 202 203 |
# File 'lib/fear/future.rb', line 201 def completed? promise.fulfilled? end |
#fallback_to(fallback) ⇒ Fear::Future
Creates a new future which holds the result of self
future if it was completed successfully, or, if not, the result of the fallback
future if fallback
is completed successfully. If both futures are failed, the resulting future holds the error object of the first future.
408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/fear/future.rb', line 408 def fallback_to(fallback) promise = Promise.new(**@options) on_complete_match do |m| m.success { |value| promise.complete!(value) } m.failure do |error| fallback.on_complete_match do |m2| m2.success { |value| promise.complete!(value) } m2.failure { promise.failure!(error) } end end end promise.to_future end |
#flat_map {|| ... } ⇒ Fear::Future
Creates a new future by applying a block to the successful result of this future, and returns the result of the function as the new future. If this future is completed with an exception then the new future will also contain this exception.
290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/fear/future.rb', line 290 def flat_map promise = Promise.new(**@options) on_complete_match do |m| m.case(Fear::Failure) { |failure| promise.complete!(failure) } m.success do |value| yield(value).on_complete { |callback_result| promise.complete!(callback_result) } rescue StandardError => error promise.failure!(error) end end promise.to_future end |
#map(&block) ⇒ Fear::Future
Creates a new future by applying a block to the successful result of this future. If this future is completed with an error then the new future will also contain this error.
264 265 266 267 268 269 270 271 |
# File 'lib/fear/future.rb', line 264 def map(&block) promise = Promise.new(**@options) on_complete do |try| promise.complete!(try.map(&block)) end promise.to_future end |
#on_complete {|| ... } ⇒ self
When this future is completed call the provided block.
If the future has already been completed, this will either be applied immediately or be scheduled asynchronously.
162 163 164 165 166 167 |
# File 'lib/fear/future.rb', line 162 def on_complete promise.add_observer do |_time, try, _error| yield try end self end |
#on_complete_match {|| ... } ⇒ self
When this future is completed match against result.
If the future has already been completed, this will either be applied immediately or be scheduled asynchronously.
182 183 184 185 186 187 |
# File 'lib/fear/future.rb', line 182 def on_complete_match promise.add_observer do |_time, try, _error| Fear::Try.matcher { |m| yield(m) }.call_or_else(try, &:itself) end self end |
#on_failure {|| ... } ⇒ self
When this future is completed with a failure apply the provided callback to the error.
If the future has already been completed with a failure, this will either be applied immediately or be scheduled asynchronously.
Will not be called in case that the future is completed with a value.
122 123 124 125 126 127 128 |
# File 'lib/fear/future.rb', line 122 def on_failure on_complete do |result| if result.failure? yield result.exception end end end |
#on_failure_match {|m| ... } ⇒ self
When this future is completed with a failure match against the error.
If the future has already been completed with a failure, this will either be applied immediately or be scheduled asynchronously.
Will not be called in case that the future is completed with a value.
144 145 146 147 148 |
# File 'lib/fear/future.rb', line 144 def on_failure_match on_failure do |error| Fear.matcher { |m| yield(m) }.call_or_else(error, &:itself) end end |
#on_success {|value| ... } ⇒ self Also known as: each
Calls the provided callback when this future is completed successfully.
If the future has already been completed with a value, this will either be applied immediately or be scheduled asynchronously.
82 83 84 85 86 |
# File 'lib/fear/future.rb', line 82 def on_success(&block) on_complete do |result| result.each(&block) end end |
#on_success_match {|m| ... } ⇒ self
When this future is completed successfully match against its result
If the future has already been completed with a value, this will either be applied immediately or be scheduled asynchronously.
100 101 102 103 104 |
# File 'lib/fear/future.rb', line 100 def on_success_match on_success do |value| Fear.matcher { |m| yield(m) }.call_or_else(value, &:itself) end end |
#recover(&block) ⇒ Fear::Future
Creates a new future that will handle any matching error that this future might contain. If there is no match, or if this future contains a valid result then the new future will contain the same.
344 345 346 347 348 349 350 351 |
# File 'lib/fear/future.rb', line 344 def recover(&block) promise = Promise.new(**@options) on_complete do |try| promise.complete!(try.recover(&block)) end promise.to_future end |
#select {|| ... } ⇒ Fear::Future
Creates a new future by filtering the value of the current future with a predicate.
If the current future contains a value which satisfies the predicate, the new future will also hold that value. Otherwise, the resulting future will fail with a NoSuchElementError
.
If the current future fails, then the resulting future also fails.
320 321 322 323 324 325 326 327 328 |
# File 'lib/fear/future.rb', line 320 def select map do |result| if yield(result) result else raise NoSuchElementError, "#select predicate is not satisfied" end end end |
#transform(success, failure) {|success, failure| ... } ⇒ Fear::Future
Creates a new future by applying the success
function to the successful result of this future, or the failure
function to the failed result. If there is any non-fatal error raised when success
or failure
is applied, that error will be propagated to the resulting future.
245 246 247 248 249 250 251 252 |
# File 'lib/fear/future.rb', line 245 def transform(success, failure) promise = Promise.new(**@options) on_complete_match do |m| m.success { |value| promise.success(success.(value)) } m.failure { |error| promise.failure(failure.(error)) } end promise.to_future end |
#value ⇒ Fear::Option<Fear::Try>
The value of this Future
.
213 214 215 |
# File 'lib/fear/future.rb', line 213 def value Fear.option(promise.value(0)) end |
#zip(other) ⇒ Fear::Future
Zips the values of self
and other
future, and creates a new future holding the array of their results.
If self
future fails, the resulting future is failed with the error stored in self
. Otherwise, if other
future fails, the resulting future is failed with the error stored in other
.
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/fear/future.rb', line 370 def zip(other) promise = Promise.new(**@options) on_complete_match do |m| m.success do |value| other.on_complete do |other_try| promise.complete!( other_try.map do |other_value| if block_given? yield(value, other_value) else [value, other_value] end end, ) end end m.failure do |error| promise.failure!(error) end end promise.to_future end |