JavaRush /Курсы /Модуль 4: FastAPI /Асинхронные концепции в Python (async, await)

Асинхронные концепции в Python (async, await)

Модуль 4: FastAPI
2 уровень , 0 лекция
Открыта

Фреймворк FastAPI полностью асинхронный, поэтому давайте для начала вспомним что же такое асинхронное программирование, и как оно работает...

Представьте, что вы шеф-повар в ресторане и готовите несколько блюд одновременно. Поставив суп на медленный огонь, вы не стоите рядом с кастрюлей 30 минут, наблюдая как он кипит. Вместо этого вы используете это время для нарезки овощей или приготовления соуса для другого блюда.

Точно так же работает асинхронное программирование. Когда ваш код запускает операцию, которая занимает время (например, запрос к базе данных или внешнему API), он не "замирает" в ожидании ответа. Вместо этого он отмечает, что нужно вернуться к этой задаче позже, и тем временем выполняет другие полезные задачи. Когда ответ готов, код возвращается и продолжает работу с полученными данными.

Ключевые слова async и await в Python позволяют организовать такой подход: async обозначает функцию, которая может "переключаться" между задачами, а await — точку, где функция может приостановиться и уступить контроль другим задачам, пока ждет завершения какой-то операции.

Асинхронное программирование — это подход, при котором задачи, требующие длительного времени на обработку, такие как запросы в базу данных или взаимодействие с веб-сервисами, не блокируют выполнение остального кода. Это повышает производительность и отзывчивость приложений.

Синхронное vs асинхронное выполнение кода

В синхронном коде каждая операция выполняется по порядку. Если одна из них занимает много времени, выполнение программы "замораживается". Асинхронный код позволяет запускать другие задачи, пока одна из операций ожидает завершения.

Пример синхронного кода:


import time

def fetch_data():
    time.sleep(3)  # Эмуляция долгой операции
    return "Данные получены"

print("Начинаем загрузку данных...")
result = fetch_data()
print(result)

Вывод:


Начинаем загрузку данных...
(3 секунды ожидания...)
Данные получены

Пример асинхронного кода:


import asyncio

async def fetch_data():
    await asyncio.sleep(3)  # Асинхронный "сон"
    return "Данные получены"

async def main():
    print("Начинаем загрузку данных...")
    result = await fetch_data()
    print(result)

asyncio.run(main())

Вывод:


Начинаем загрузку данных...
(3 секунды ожидания без блокировки других задач)
Данные получены

Асинхронные функции в Python

Асинхронное программирование в Python базируется на двух ключевых концепциях, о которых мы уже успели упомянуть: async и await. Давайте разберемся в них подробнее.

Ключевое слово async перед функцией указывает, что эта функция является асинхронной. Её вызов возвращает специальный объект — корутину (coroutine), а не результат сразу.


async def my_async_function():
    return "Привет, асинхронный мир!"

# Вызов функции возвращает корутину
coro = my_async_function()
print(coro)  # <coroutine object my_async_function ...>

Ключевое слово await используется внутри асинхронных функций для ожидания выполнения другой корутины. Оно "ставит на паузу" выполнение текущей функции до завершения корутины, освобождая ресурсы для других задач.


import asyncio

async def my_async_function():
    await asyncio.sleep(1)  # Ожидание 1 секунду
    return "Завершено!"

async def main():
    result = await my_async_function()
    print(result)

asyncio.run(main())

Важное предостережение

Асинхронные функции нельзя вызывать напрямую в синхронном коде! Всегда используйте asyncio.run() или другие подходы для запуска корутин.


Понимание Event Loop (цикла событий)

Event Loop (цикл событий) — это "дирижёр" асинхронного оркестра. Он управляет выполнением всех запущенных корутин, переключаясь между ними, пока одна из них ожидает результата.

Когда вы вызываете asyncio.run(), Python создаёт event loop и запускает его. Это позволяет программе выполнять несколько задач одновременно.

Пример работы Event Loop

Давайте посмотрим на простой пример с несколькими задачами:


import asyncio

async def task_one():
    print("Задача 1: Начало")
    await asyncio.sleep(2)
    print("Задача 1: Конец")

async def task_two():
    print("Задача 2: Начало")
    await asyncio.sleep(1)
    print("Задача 2: Конец")

async def main():
    await asyncio.gather(task_one(), task_two())

asyncio.run(main())

Вывод программы:


Задача 1: Начало
Задача 2: Начало
Задача 2: Конец
Задача 1: Конец

Здесь await asyncio.gather() запускает обе задачи одновременно. Event Loop переключается между задачами, когда одна из них "спит".


Асинхронность в реальных сценариях

Асинхронность наиболее полезна в следующих случаях:

  • Работа с внешними API (например, отправка HTTP-запросов)
  • Взаимодействие с базами данных
  • Обработка файловых операций

Приведём пример асинхронного HTTP-запроса. Используем библиотеку httpx для отправки асинхронных запросов:


import httpx
import asyncio

async def get_data():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://jsonplaceholder.typicode.com/todos/1')
        print(response.json())

asyncio.run(get_data())

Вывод программы:


{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}

Типичные ошибки и подводные камни

Асинхронность в Python — это мощный инструмент, но и он неидеален. Вот проблемы, которые часто встречаются у новичков:

  1. Попытка вызвать асинхронную функцию в синхронном коде.

    Асинхронную функцию нельзя запустить без event loop:

    
    async def my_function():
        return "Ошибка"
    
    my_function()  # Ошибка! Нельзя вызывать напрямую.
    
    Вместо этого используйте:
    
    asyncio.run(my_function())
    
  2. Пропущенное ключевое слово await.

    Если вы забудете await, корутина не будет выполнена (и Python даже не ругнётся):

    
    async def my_function():
        await asyncio.sleep(1)
        print("Привет!")
    
    async def main():
        my_function()  # Нет await — ничего не произойдёт.
        await my_function()  # Всё будет ок.
    
    asyncio.run(main())
    
  3. Блокирующий код в асинхронной функции.
    Если внутри асинхронной функции написать блокирующий код (например, time.sleep() вместо await asyncio.sleep()), это "заморозит" event loop.

Применение асинхронности в FastAPI

Асинхронность в Python отлично сочетается с FastAPI. В следующей лекции мы создадим наш первый асинхронный эндпоинт с GET-запросом. Это позволит вашим API обрабатывать тысячи запросов одновременно, при этом оставаясь быстрыми и отзывчивыми.

Важно запомнить, что каждая асинхронная функция, которую вы пишете в FastAPI, выполняется в рамках одного event loop, управляемого сервером Uvicorn.

На этом этапе вы готовы расширить свои знания FastAPI, добавляя асинхронные операции в свои API. В следующей лекции мы погрузимся в создание реальных асинхронных эндпоинтов.

1
Задача
Модуль 4: FastAPI, 2 уровень, 0 лекция
Недоступна
Одновременное выполнение нескольких задач
Одновременное выполнение нескольких задач
1
Задача
Модуль 4: FastAPI, 2 уровень, 0 лекция
Недоступна
Асинхронный HTTP-запрос с выводом данных
Асинхронный HTTP-запрос с выводом данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ