user10597450
user10597450

Reputation:

Correctly using loop.create_future

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

Answers (1)

user4815162342
user4815162342

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

Related Questions