Step 7 - The Future¶
Looking at our current processing chain loop
-> yield from*
-> yield
(the *
means it can be repeated n times) the yield
part on the right side is a bit
out-of-order. It just doesn’t feel right completely. Wouldn’t it be better if
get a well defined API that abstracts the yield
and yield from
can be used
instead.
Let us try something. How does a coroutine work normally? First it runs and starts something. Afterwards it suspends itself and gives back control to the caller. If something is done and it is resumed, a result is returned.
from typing import Any, Generator
class Future:
"""Return a result in the future v1"""
_result = None
def set_result(self, result: Any):
self._result = result
def __iter__(self) -> Generator[None, None, Any]:
yield
return self._result
We introduce a new class Future
that can be called with yield from future
to first suspend and give the control back to the caller and second to return a
result. Additionally a Future
can be in one of the following states:
Pending - Waiting for a result
Done - A result is set
It always takes (at least) two generator steps to get the into the done state.
Note
As a side note, the Future class implements the Iterable
protocol.
Example:
def future_result(future):
# something might happen here too
return (yield from future)
future = Future()
coroutine = future_result(future)
next(coroutine)
# the result of the future is set somewhere
# we just fake setting it here
future.set_result(123)
try:
next(coroutine)
except StopIteration as e:
result = e.value
print(result)
Hint
To clarify how the Future
works, future_result
could be rewritten as
def future_result(future):
# __iter__ implements the Iterable protocol and
# returns a coroutine/generator/iterator
coroutine = future.__iter__()
return (yield from coroutine)
or even more detailed without yield from
def future_result(future):
# __iter__ implements the Iterable protocol and
# returns a coroutine/generator/iterator
coroutine = future.__iter__()
try:
while True:
x = next(coroutine)
yield x
except StopIteration as e:
return e.value
As a result we have an object that allows to set a result (even) in a future step.
Example Usage:
def do_something(future):
# do something, for example:
# * start a thread
# * wait for something being returned from the thread via a callback
def on_result_from_thread(result):
future.set_result(result)
thread = thread.create()
thread.on_exit = on_result_from_thread
def some_result():
future = Future()
do_something(future)
return (yield from future)
Simple example without real purpose:
from future import Future
from loop import loop
def some_result(value):
# just a coroutine to return some value
future = Future()
# do something, we just fake it here
future.set_result(value)
return (yield from future)
def add(coroutine1, coroutine2):
x = yield from coroutine1
y = yield from coroutine2
return x + y
def main():
return (yield from add(some_result(1), some_result(2)))
result = loop(main())
print("Loop finished with result", result)
Output:
Loop step 1
Loop step 2
Loop step 3
Loop finished with result 3
Summary
We learned the basic concept of a
Future