Можно ли подождать завершения таски через await после ее создания
Что проверяют
Различаете ли вы корутину и задачу, и понимаете ли, что await не запускает выполнение, а только дожидается результата. Обычно дальше идёт продолжение про то, зачем вообще разделять создание и ожидание.
Как отвечать
Да, это штатный и самый частый сценарий. Вызов asyncio.create_task(coro()) оборачивает корутину в задачу и планирует её в текущем цикле событий: выполняться она начнёт при ближайшей передаче управления циклу, даже если её никто не ждёт. А await task просто подписывается на результат.
task = asyncio.create_task(fetch(url))
other = await do_something()
data = await taskСмысл разделения в том, чтобы запустить несколько операций и подождать их вместе, для этого есть asyncio.gather и группы задач. Если написать await asyncio.create_task(x()) одной строкой, получится ровно то же, что await x(), никакого выигрыша.
Полезные детали, которые отличают уверенный ответ: задачу можно ждать повторно, второй await вернёт тот же результат, тогда как корутину дважды ждать нельзя. Исключение внутри задачи всплывает в точке ожидания, а если ожидания так и не случилось, интерпретатор пожалуется на неполученное исключение при сборке мусора. Отмена делается через task.cancel(), внутри она приходит как CancelledError, и глотать её молча нельзя.
Где спотыкаются
Создают задачу и тут же её ждут, считая, что получили конкурентность. Она появляется только когда между созданием и ожиданием есть другая работа или когда задач несколько.
Второе: не хранят ссылку на задачу, запущенную по принципу «отправил и забыл». Цикл событий держит на неё только слабую ссылку, и сборщик мусора может убить её посреди работы. Ссылки складывают в множество и убирают в колбэке завершения.
Третье: путают ожидание с блокировкой и зовут внутри задачи синхронный код: time.sleep, обычный сетевой клиент, тяжёлый расчёт. Цикл встаёт целиком, и все остальные задачи ждут. Такое выносят в поток через asyncio.to_thread.