Как я могу запланировать ожидания для последовательного выполнения без ожидания? - PullRequest
0 голосов
/ 26 августа 2018

Предположим, у меня есть несколько асинхронных функций, f1, f2 и f3. Я хочу выполнить эти функции в последовательном порядке. Самый простой способ сделать это - подождать их:

async def foo():
    await f1()
    # Do something else
    await f2()
    # Do something else
    await f3()
    # Do something else

Однако меня не волнуют результаты этих асинхронных функций, и я хотел бы продолжить выполнение остальной части функции после планирования асинхронных функций.

Из документации по asyncio кажется, что asyncio.ensure_future() может помочь мне в этом. Я использовал следующий код, чтобы проверить это, и синхронные части foo() согласно моим ожиданиям. Однако bar() никогда не выполняется после asyncio.sleep()

import asyncio

async def bar(name):
    print(f'Sleep {name}')
    await asyncio.sleep(3)
    print(f'Wakeup {name}')

async def foo():
    print('Enter foo')

    for i in [1, 2, 3]:
        asyncio.ensure_future(bar(i))
        print(f'Scheduled bar({i}) for execution')

    print('Exit foo')

loop = asyncio.get_event_loop()
loop.run_until_complete(foo())

Выход для вышеуказанного кода:

Enter foo
Scheduled bar(1) for execution
Scheduled bar(2) for execution
Scheduled bar(3) for execution
Exit foo
Sleep 1
Sleep 2
Sleep 3

Итак, как правильно делать то, что я ищу?

1 Ответ

0 голосов
/ 26 августа 2018

У меня есть несколько асинхронных функций, f1, f2 и f3. Я хочу выполнить эти функции в последовательном порядке. [...] Я хотел бы продолжить выполнение остальной части функции после планирования асинхронных функций.

Простой способ сделать это - использовать вспомогательную функцию и запустить ее в фоновом режиме:

async def foo():
    async def run_fs():
        await f1()
        await f2()
        await f3()
    loop = asyncio.get_event_loop()
    loop.create_task(run_fs())
    # proceed with stuff that foo needs to do
    ...

create_task отправляет сопрограмму в цикл событий. Вы также можете использовать ensure_future для этого, но create_task является предпочтительным при порождении сопрограммы.

Код в вопросе имеет две проблемы: во-первых, функции выполняются не последовательно, а параллельно. Это исправлено, как показано выше, запустив одну асинхронную функцию в фоновом режиме, которая ожидает три по порядку. Вторая проблема заключается в том, что в asyncio run_until_complete(foo()) ожидает только завершения foo(), а не задач, порожденных foo (хотя существуют альтернативы asyncio , которые address this ). Если вы хотите, чтобы run_until_complete(foo()) дождался окончания run_fs, foo должен ждать его сам.

К счастью, реализовать это тривиально - просто добавьте еще await в конце foo(), ожидая задачи, созданной для run_fs ранее. Если задача уже выполнена к этому моменту, await немедленно завершится, в противном случае он будет ждать.

async def foo():
    async def run_fs():
        await f1()
        await f2()
        await f3()
    loop = asyncio.get_event_loop()
    f_task = loop.create_task(run_fs())
    # proceed with stuff that foo needs to do
    ...
    # finally, ensure that the fs have finished by the time we return
    await f_task
...