lib/async/task.rb in async-2.2.1 vs lib/async/task.rb in async-2.3.0

- old
+ new

@@ -109,38 +109,43 @@ else raise RuntimeError, "Task already running!" end end + # Run an asynchronous task as a child of the current task. def async(*arguments, **options, &block) + raise "Cannot create child task within a task that has finished execution!" if self.finished? + task = Task.new(self, **options, &block) task.run(*arguments) return task end - # Retrieve the current result of the task. Will cause the caller to wait until result is available. - # @raises[RuntimeError] If the task's fiber is the current fiber. + # Retrieve the current result of the task. Will cause the caller to wait until result is available. If the result was an exception, raise that exception. + # + # Conceptually speaking, waiting on a task should return a result, and if it throws an exception, this is certainly an exceptional case that should represent a failure in your program, not an expected outcome. In other words, you should not design your programs to expect exceptions from `#wait` as a normal flow control, and prefer to catch known exceptions within the task itself and return a result that captures the intention of the failure, e.g. a `TimeoutError` might simply return `nil` or `false` to indicate that the operation did not generate a valid result (as a timeout was an expected outcome of the internal operation in this case). + # + # @raises [RuntimeError] If the task's fiber is the current fiber. # @returns [Object] The final expression/result of the task's block. def wait - raise "Cannot wait on own fiber" if Fiber.current.equal?(@fiber) + raise "Cannot wait on own fiber!" if Fiber.current.equal?(@fiber) if running? @finished ||= Condition.new @finished.wait end - case @result - when Exception + if @result.is_a?(Exception) raise @result else return @result end end - # Access the result of the task without waiting. May be nil if the task is not completed. + # Access the result of the task without waiting. May be nil if the task is not completed. Does not raise exceptions. attr :result # Stop the task and all of its children. def stop(later = false) if self.stopped? @@ -266,10 +271,10 @@ # Attempt to remove this node from the task tree. consume # If this task was being used as a future, signal completion here: if @finished - @finished.signal(@result) + @finished.signal(self) end end # Set the current fiber's `:async_task` to this task. def set!