Reputation:
I was reading the Python documentation and PyMotW book trying to learn Async/Await, Futures, and Tasks.
Coroutines and Tasks documentation:
Normally there is no need to create Future objects at the application level code.
From the future documentation it states the following:
loop.create_future()
Create an asyncio.Future object attached to the event loop.
This is the preferred way to create Futures in asyncio. This lets third-party event loops provide alternative implementations of the Future object (with better performance or instrumentation).
However, in PyMotW chapter on Future, the author creates a future
object like this:
all_done = asyncio.Future()
I assume because the book is slightly behind the current version of Python. To correct this, I did the following:
future_Obj = event_loop.create_future()
So the authors full code becomes:
import asyncio
def mark_done(future, result):
print('setting future result to {!r}'.format(result))
future.set_result(result)
event_loop = asyncio.get_event_loop()
try:
future_Obj = event_loop.create_future()
print('scheduling mark_done')
event_loop.call_soon(mark_done, future_Obj, 'the result')
print('entering event loop')
result = event_loop.run_until_complete(future_Obj)
print('returned result: {!r}'.format(result))
finally:
print('closing event loop')
event_loop.close()
print('future result: {!r}'.format(future_Obj.result()))
Question:
Is future_Obj = event_loop.create_future()
in the sample above, the correct way to create a future
object according to the documentation?
Upvotes: 16
Views: 13196
Reputation: 154846
Is
future_Obj = event_loop.create_future()
in the sample above, the correct way to create a future object according to the documentation?
Yes, in the code as shown, that is exactly the way to do it.
One thing to watch out for is that a future is tied to an event loop, so creating a future at top-level creates a future tied to the loop that asyncio.get_event_loop()
returns initially. Once you switch to asyncio.run
, you will get an error because each invocation of asyncio.run
creates a new event loop.
To avoid that issue, a top-level future can start off as None
and be created inside a coroutine, using global
as appropriate. And since you're passing the future explicitly (which is a good practice to follow), you don't need a global variable at all:
def mark_done(future, result):
print('setting future result to {!r}'.format(result))
future.set_result(result)
async def main():
loop = asyncio.get_event_loop()
future = loop.create_future()
print('scheduling mark_done')
loop.call_soon(mark_done, future, 'the result')
print('suspending the coroutine')
result = await future
print('awaited result: {!r}'.format(result))
print('future result: {!r}'.format(future.result()))
return result
if __name__ == '__main__':
print('entering the event loop')
result = asyncio.run(main())
print('returned result: {!r}'.format(result))
Note that when using asyncio.run
, you never need to explicitly close the loop, that is done automatically. If you are using Python 3.6 or earlier, you can replace asyncio.run(main())
with asyncio.get_event_loop().run_until_complete(main())
.
Upvotes: 8