← Назад к вопросам

Что будет если не использовать await в асинхронной функции?

1.8 Middle🔥 291 комментариев
#DevOps и инфраструктура

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что будет если не использовать await в асинхронной функции?

Это критичный баг, который приводит к проблемам с race conditions и потере данных. Давайте разберёмся.

Основное правило

Если функция асинхронная — её результат нужно await-ить!

async def fetch_user(user_id: int):
    # Имитируем задержку БД (1 сек)
    await asyncio.sleep(1)
    return {"id": user_id, "name": "John"}

async def main():
    # НЕПРАВИЛЬНО: забыли await
    result = fetch_user(1)  # Возвращает Coroutine объект
    print(result)  # <coroutine object fetch_user at 0x7f8b8c8c5f40>
    print(type(result))  # <class coroutine>

    # ПРАВИЛЬНО: используем await
    result = await fetch_user(1)
    print(result)  # {"id": 1, "name": "John"}

Что происходит без await

При отсутствии await функция не выполняется. Вместо этого возвращается Coroutine объект — это как повисшая задача.

import asyncio
from datetime import datetime

async def slow_operation():
    print(f"[{datetime.now()}] Операция началась")
    await asyncio.sleep(2)  # Долгая операция
    print(f"[{datetime.now()}] Операция завершена")
    return "Результат"

async def main():
    print(f"[{datetime.now()}] main() начался")
    
    # БЕЗ await
    coro = slow_operation()  # Корутина создана, но не выполняется!
    print(f"[{datetime.now()}] После вызова slow_operation()")
    
    # Консоль:
    # [12:00:00] main() начался
    # [12:00:00] После вызова slow_operation()
    # Заметь: "Операция начала" НЕ печатается!
    # slow_operation() никогда не выполнилась!

asyncio.run(main())

Проблемы в реальном коде

Проблема 1: Потеря данных

async def save_to_database(user_data: dict):
    await asyncio.sleep(1)  # Имитация запроса БД
    print(f"Пользователь {user_data[name]} сохранён")

async def register_user(name: str):
    user = {"name": name, "email": f"{name}@example.com"}
    
    # БЕЗ await — функция не выполнится!
    save_to_database(user)  # ❌ НЕПРАВИЛЬНО
    
    return f"Пользователь {name} зарегистрирован"

async def main():
    result = await register_user("John")
    print(result)  # Пользователь John зарегистрирован
    # Но в БД НИЧЕГО не сохранилось!
    # Печать "Пользователь ... сохранён" не появится

asyncio.run(main())

Внешне всё выглядит нормально, но данные потеряны.

Проблема 2: RuntimeWarning о ненераспределённой корутине

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

async def main():
    async_task()  # ❌ Забыли await
    # Вывод:
    # RuntimeWarning: coroutine async_task was never awaited

asyncio.run(main())

Python предупреждает, что корутина никогда не была выполнена.

Проблема 3: Race conditions с несколькими задачами

async def fetch_user(user_id: int):
    await asyncio.sleep(1)
    return {"id": user_id, "name": f"User {user_id}"}

async def fetch_multiple():
    # БЕЗ await — создаём 3 корутины, но НЕ выполняем их
    user1 = fetch_user(1)
    user2 = fetch_user(2)
    user3 = fetch_user(3)
    
    print(type(user1))  # <class coroutine>
    
    # Возвращаем объекты, а не данные!
    return [user1, user2, user3]  # ❌ НЕПРАВИЛЬНО

async def main():
    result = await fetch_multiple()
    print(result)  # [<coroutine...>, <coroutine...>, <coroutine...>]
    # Данные НЕ получены!

asyncio.run(main())

Правильные способы

Способ 1: Простой await

async def main():
    result = await fetch_user(1)
    print(result)  # Данные получены

Способ 2: asyncio.gather() для нескольких задач

async def main():
    # Правильно: все задачи выполняются параллельно
    results = await asyncio.gather(
        fetch_user(1),
        fetch_user(2),
        fetch_user(3)
    )
    print(results)  # [user1_data, user2_data, user3_data]

Способ 3: asyncio.create_task() для фоновых задач

async def background_task():
    await asyncio.sleep(5)
    print("Фоновая задача выполнена")

async def main():
    # Создаём задачу БЕЗ ожидания
    task = asyncio.create_task(background_task())
    
    print("Main продолжает работу")
    
    # Но позже нужно дождаться завершения!
    await task

Способ 4: Проверка типа перед использованием

import asyncio
from inspect import iscoroutine

async def my_func():
    return "Done"

async def main():
    result = my_func()  # Корутина
    
    # Проверяем тип
    if iscoroutine(result):
        result = await result  # Если корутина — await-им
    
    print(result)

asyncio.run(main())

Ошибки типа TypeChecker

# mypy может поймать такие ошибки (если включена проверка)

async def async_func() -> int:
    return 42

async def main() -> None:
    result = async_func()  # mypy error: Coroutine type expected
    # mypy скажет: "Забыл await?"

Отличие Coroutine от Task

async def my_async():
    return "result"

async def main():
    # Coroutine — объект, который может быть выполнен
    coro = my_async()
    
    # Task — Coroutine, который уже был запланирован
    task = asyncio.create_task(my_async())
    # Task начнёт выполняться сразу (в следующем событии цикла)
    # Coroutine выполнится только когда его await-иш

Как это связано с FastAPI

from fastapi import FastAPI
from sqlalchemy import select

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # Если БЕЗ await
    user = fetch_user_from_db(user_id)  # ❌ Забыли await!
    # user будет Coroutine, а не данные!
    
    return user  # Вернём некорректный объект

# Клиент получит:
# {"detail": "Object of type coroutine is not JSON serializable"}

Итоговый чек-лист

# ✓ Правильно: используй await для всех async функций
result = await async_function()

# ✓ Правильно: используй asyncio.gather() для параллельных задач
results = await asyncio.gather(task1(), task2(), task3())

# ✓ Правильно: используй asyncio.create_task() для фоновых задач
task = asyncio.create_task(background_work())

# ❌ НЕПРАВИЛЬНО: забыли await
result = async_function()

# ❌ НЕПРАВИЛЬНО: забыли await перед gather
results = asyncio.gather(task1(), task2())

# ❌ НЕПРАВИЛЬНО: забыли await перед create_task
task = asyncio.create_task(background_work())

Золотое правило

Если видишь async def — всегда используй await при вызове!

Иное приводит к:

  • Потере данных
  • Race conditions
  • RuntimeWarning
  • Неопределённому поведению
  • Трудноуловимым багам в production

Просто всегда помни: async функция без await = корутина, которая никогда не выполнится.