0

I ran into an asyncIO problem. I want to have my program do something while a task a coroutine is being run, and after the coroutine is done, exit.

I've simplified my problem into this snippet:

import time
import asyncio


async def long_task():
    time.sleep(3)
    return "DONE !"


def do_while_idle():
    loop = asyncio.get_event_loop()
    task = loop.create_task(long_task)
    while not task.done():
        time.sleep(1)
        print("IDLE")
    print(task.result())


do_while_idle()

I'd like to have this output:

IDLE
IDLE
IDLE
DONE !

Thanks in advance.

1
  • 3
    Don't use time.sleep(3) in an asyncio loop. Use asyncio.sleep. Commented Apr 28, 2020 at 17:44

2 Answers 2

1

As MisterMiyagi points out, you must await asyncio.sleep() instead of calling time.sleep(), as well as eliminate other blocking, e.g. by replacing the use of requests with aiohttp.

To do some idle code for as long as a task is running, you can write the idle code as an infinite loop, independent of the main task. do_while_idle can start the idle loop as a task, directly await long_task(), and then just cancel the idle one:

import asyncio

async def long_task():
    await asyncio.sleep(3)
    return "DONE !"

async def idle():
    while True:
        await asyncio.sleep(1)
        print("IDLE")

async def do_while_idle():
    idle_task = asyncio.create_task(idle())
    print(await long_task())
    idle_task.cancel()

asyncio.run(do_while_idle())
Sign up to request clarification or add additional context in comments.

1 Comment

Exactly this! Thank you very much.
1

You can approach this like so:

import asyncio

async def do_while_idle():
    for _ in range(3):
        print("IDLE")
        await asyncio.sleep(1)

    return "Done stuff while idling"

async def long_task():
    await asyncio.sleep(3)
    return "DONE !"

async def main():
    task = asyncio.create_task(long_task())
    task2 = asyncio.create_task(do_while_idle())
    done, _ = await asyncio.wait({task, task2})

    if task in done:
        task2.cancel()
        print(task.result())

asyncio.run(main())

Idling should be another task, this snippet waits for the long_task to be finished, and cancels the idling task. You can also check if both idling and long_task is finished like so:

async def main():
    task = asyncio.create_task(long_task())
    task2 = asyncio.create_task(do_while_idle())
    done, _ = await asyncio.wait({task, task2})

    if task and task2 in done:
        print(task.result(), task2.result())

First code snippet will give you the result you want:

IDLE
IDLE
IDLE
DONE !

5 Comments

Thank you for your time. But it is not quite what I need. I need do_while_idle() to be return/print something every one second, no matter how long "long_task" run. Your code snippet only works because you hardcoded "3" in both coro.
@daolf You can replace for _ in range(3) with while True and it should work just as well.
I think it won't because asyncio.wait wait for all task to be finished.
@daolf Yes you can replace it with while True, because in the first code snippet, when the long_task gets completed, task2 is being cancelled
@daolf Good point. It could be fixed by using return_when=FIRST_COMPLETED, but it can be expressed without wait() at all. I've written it up as an answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.