Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why asyncio.wait does not wait for the FIRST_COMPLETED

I'am new to Python 3.5 asyncio.

In my code below the asyncio.wait() does not wait for the stop_future to complete:

import asyncio
import datetime
from concurrent.futures import FIRST_COMPLETED


def stop():  # callback after 12 seconds

    print('stop', datetime.datetime.now())
    stop_future.set_result('Done!')


async def display_dt():

    while not stop_future.done():
        print('dt-1', datetime.datetime.now())
        # sleep 5 seconds or stop_future done
        asyncio.wait([await asyncio.sleep(5), stop_future],  return_when=FIRST_COMPLETED)
        print('dt-2', datetime.datetime.now())

    task = asyncio.Task.current_task()
    task.cancel()
    print(stop_future.result())


loop = asyncio.get_event_loop()
stop_future = asyncio.Future()
loop.call_later(12, stop)
loop.run_until_complete(display_dt())
loop.close() 

Result:

dt-1 2015-11-08 00:49:37.324582
dt-2 2015-11-08 00:49:42.325503
dt-1 2015-11-08 00:49:42.325503
dt-2 2015-11-08 00:49:47.326423
dt-1 2015-11-08 00:49:47.326423
stop 2015-11-08 00:49:49.327192  # async.wait stop_future not triggered
dt-2 2015-11-08 00:49:52.327343  # while loop finishes here
>>> Done!

Update:

Below is the code for the updated function display_dt. Now asyncio.wait works fine.
But I do not understand why the above code with the coro does not work ??

@asyncio.coroutine    # decorator necessary? It works fine without 
def display_dt():

    while not stop_future.done():
        print('dt-1', datetime.datetime.now())
        yield from asyncio.wait([asyncio.sleep(5), stop_future], return_when=FIRST_COMPLETED)
        print('dt-2', datetime.datetime.now())

    task = asyncio.Task.current_task()
    task.cancel()
    print(stop_future.result())

Result:

dt-1 2015-11-08 01:19:06.289915
dt-2 2015-11-08 01:19:11.290836
dt-1 2015-11-08 01:19:11.290836
dt-2 2015-11-08 01:19:16.291757
dt-1 2015-11-08 01:19:16.291757
stop 2015-11-08 01:19:18.292525
dt-2 2015-11-08 01:19:18.292525  # async wait stop_future triggered
Done!

Update: @asyncio.coroutine # decorator necessary?

I found the answer in this great chapter:

The @asyncio.coroutine decorator is not magical. In fact, if it decorates a generator function and the PYTHONASYNCIODEBUG environment variable is not set, the decorator does practically nothing. It just sets an attribute, _is_coroutine, for the convenience of other parts of the framework. It is possible to use asyncio with bare generators not decorated with @asyncio.coroutine at all.↩

like image 940
voscausa Avatar asked Dec 30 '25 01:12

voscausa


1 Answers

You must await the .wait() coroutine, not await the coroutines you pass to .wait():

async def display_dt():

    while not stop_future.done():
        print('dt-1', datetime.datetime.now())
        # sleep 5 seconds or stop_future done
        await asyncio.wait([asyncio.sleep(5), stop_future], return_when=FIRST_COMPLETED) # <----
        print('dt-2', datetime.datetime.now())

    task = asyncio.Task.current_task()
    task.cancel()
    print(stop_future.result())

Now this is equivalent to your edit, but using a native coroutine (i.e. the async/await syntax).

Update showing the result:

dt-1 2015-11-08 13:14:21.910399
dt-2 2015-11-08 13:14:26.911320
dt-1 2015-11-08 13:14:26.911320
dt-2 2015-11-08 13:14:31.912240
dt-1 2015-11-08 13:14:31.912240
stop 2015-11-08 13:14:33.913009
dt-2 2015-11-08 13:14:33.913009
Done!
like image 196
Jashandeep Sohi Avatar answered Dec 31 '25 14:12

Jashandeep Sohi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!