2025, Oct 17 11:16

Как запускать asyncio-код в Jupyter Notebook без ошибок

Разбираем ошибку RuntimeError при asyncio.run в Jupyter Notebook: почему конфликтует событийный цикл и как правильно запускать корутины с top-level await.

Запуск asyncio‑кода внутри Jupyter Notebook часто сбивает с толку. Обычный для скриптов на Python приём — вызов asyncio.run() — в ноутбуках приводит к RuntimeError. Причина проста, и решение тоже — важно лишь корректно работать с ноутбуком, чтобы не конфликтовать с его событийным циклом.

Как воспроизвести проблему

Этот фрагмент работает в отдельном .py‑скрипте, но вызывает ошибку в ячейке ноутбука:

import asyncio

async def do_job():
    await asyncio.sleep(1)
    return "Done"

asyncio.run(do_job())

В среде Jupyter это обычно завершается так:

RuntimeError: Event loop is closed

Что происходит на самом деле

Под капотом Jupyter уже запущен событийный цикл. asyncio.run() пытается создать и полностью управлять новым циклом с нуля. Когда цикл уже активен, запуск ещё одного не допускается, и этот конфликт и приводит к увиденной ошибке. В обычном .py‑скрипте никакого цикла нет, пока вы его не запустите, поэтому asyncio.run() без помех создаёт его и затем корректно завершает.

Как корректно запускать асинхронный код в Jupyter

Не запускайте новый цикл. Просто ожидайте корутину напрямую в ячейке. Jupyter поддерживает top-level await, поэтому можно вызывать асинхронные функции без обёртки в asyncio.run().

import asyncio

async def do_job():
    await asyncio.sleep(1)
    return "Done"

await do_job()

Так вы используете уже существующий цикл ноутбука и полностью избегаете конфликта.

Почему это важно

Поведение асинхронного кода зависит от среды выполнения. В .py‑скрипте вызов asyncio.run() уместен, потому что активного цикла ещё нет, пока вы его не создадите. В Jupyter Notebook цикл уже работает, и попытка запустить ещё один через asyncio.run() завершится неудачей. Понимание, где вы находитесь, избавляет от запутанных ошибок и помогает писать асинхронный код, который выполняется предсказуемо.

Вывод

В ноутбуке используйте top-level await и вызывайте корутину напрямую. В обычном скрипте управляйте циклом через asyncio.run(). Этого простого разграничения достаточно, чтобы асинхронные сценарии стабильно работали в обеих средах.

Статья основана на вопросе на StackOverflow от Salika Ansari и ответе от Yokozuna.