Фреймворк 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 — это мощный инструмент, но и он неидеален. Вот проблемы, которые часто встречаются у новичков:
- Попытка вызвать асинхронную функцию в синхронном коде.
Асинхронную функцию нельзя запустить без event loop:
Вместо этого используйте:async def my_function(): return "Ошибка" my_function() # Ошибка! Нельзя вызывать напрямую.asyncio.run(my_function()) - Пропущенное ключевое слово
await.Если вы забудете
await, корутина не будет выполнена (и Python даже не ругнётся):async def my_function(): await asyncio.sleep(1) print("Привет!") async def main(): my_function() # Нет await — ничего не произойдёт. await my_function() # Всё будет ок. asyncio.run(main()) - Блокирующий код в асинхронной функции.
Если внутри асинхронной функции написать блокирующий код (например,time.sleep()вместоawait asyncio.sleep()), это "заморозит" event loop.
Применение асинхронности в FastAPI
Асинхронность в Python отлично сочетается с FastAPI. В следующей лекции мы создадим наш первый асинхронный эндпоинт с GET-запросом. Это позволит вашим API обрабатывать тысячи запросов одновременно, при этом оставаясь быстрыми и отзывчивыми.
Важно запомнить, что каждая асинхронная функция, которую вы пишете в FastAPI, выполняется в рамках одного event loop, управляемого сервером Uvicorn.
На этом этапе вы готовы расширить свои знания FastAPI, добавляя асинхронные операции в свои API. В следующей лекции мы погрузимся в создание реальных асинхронных эндпоинтов.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ