Асинхронность в Python: синтаксис и особенности

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

Здесь и появляется асинхронность — подход, который позволяет программе не «застывать» в ожидании долгих операций.

Что такое асинхронность простыми словами

Для примера понимания асинхронности, представьте официанта в ресторане.

  • Синхронная модель: официант обслуживает одного клиента за раз. Пока первый гость не получит заказ, второй ждет.
  • Асинхронная модель: официант принимает заказ у одного, пока тот ждёт еду, обслуживает следующего и так далее. Когда еда готова, он возвращается к первому клиенту.

Точно так же работает асинхронность в Python: пока одна операция «занята» (например, ждёт ответ от сервера или идет чтение файла с диска), программа может выполнять другие задачи.

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

Обычный Python-код (синхронный) выполняется пошагово, и каждая строка блокирует выполнение следующей, пока не завершится. Если программа загружает файл из сети, она может «замереть» на несколько секунд.

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

Асинхронность нужна в первую очередь там, где много ввода-вывода (I/O): работа с сетью, файлами, базами данных, API. Например:

  • работе с веб-запросами (HTTP, API);
  • взаимодействии с базами данных;
  • чтении и записи файлов;
  • обработке большого количества соединений (например, в чат-серверах).

Во всех этих случаях асинхронность позволяет не ждать, пока завершится один запрос, а запускать сразу несколько.

С чего все началось

До появления встроенной поддержки асинхронности в Python разработчики использовали многопоточность (threading) и многопроцессорность (multiprocessing).

Эти подходы позволяли выполнять несколько задач одновременно, но имели свои ограничения:

  • потоки требуют ручной синхронизации (например, через блокировки Lock);
  • при большом количестве потоков растут затраты памяти и контекстные переключения становятся дорогими;
  • из-за GIL (Global Interpreter Lock) Python-потоки не выполняются параллельно на разных ядрах процессора — по сути, в каждый момент времени работает только один поток Python-кода.

Появление асинхронного программирования (async/await) в Python 3.5 стало ответом на эти проблемы.
Асинхронность не создаёт новые потоки — вместо этого она переключает выполнение между задачами внутри одного потока, когда текущая задача ожидает результат ввода-вывода (например, сетевой запрос или чтение файла).

Это делает код более эффективным при работе с большим количеством операций ввода-вывода.

Асинхронность в Python

С версии Python 3.5 в язык был добавлен современный синтаксис для асинхронного программирования — ключевые слова async и await.

Они позволяют писать асинхронный код так же просто, как обычный, но с возможностью приостанавливать выполнение на время ожидания.

Ключевые понятия и синтаксис

1. async def

Функции, объявленные с ключевым словом async, называются корутинами (coroutines). Они не выполняются сразу, а возвращают объект, который можно дождаться с помощью await.

Пример:

async def greet():
    return "Привет!"

Если вызвать просто greet(), программа не выведет строку, а вернет корутину:

print(greet())  # <coroutine object greet at 0x...>

Чтобы получить результат, нужно использовать await внутри другой корутины:

async def main():
    message = await greet()
    print(message)

asyncio.run(main())

2. await

Ключевое слово await означает:

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

Использовать await можно только внутри async-функции.

3. asyncio.run()

Это точка входа в асинхронный мир. Она запускает цикл событий (event loop) и управляет выполнением всех корутин. Без неё асинхронный код не запустится.

4. asyncio.create_task() и asyncio.gather()

  • create_task() создаёт задачу (Task) и сразу планирует её выполнение;
  • gather() позволяет дождаться выполнения нескольких задач одновременно.

Пример:

async def foo():
    await asyncio.sleep(1)
    print("foo завершена")

async def bar():
    await asyncio.sleep(2)
    print("bar завершена")

async def main():
    task1 = asyncio.create_task(foo())
    task2 = asyncio.create_task(bar())
    await asyncio.gather(task1, task2)

asyncio.run(main())

Асинхронность на практике

Посмотрим простой пример для сравнения.

Синхронный пример

import time

def download_data(name):
    print(f"Начинаю загрузку {name}")
    time.sleep(2)  # имитация долгой операции
    print(f"Загрузка {name} завершена")

def main():
    download_data("файл 1")
    download_data("файл 2")
    download_data("файл 3")

if __name__ == "__main__":
    main()

Результат:

Начинаю загрузку файл 1
Загрузка файл 1 завершена
Начинаю загрузку файл 2
Загрузка файл 2 завершена
Начинаю загрузку файл 3
Загрузка файл 3 завершена

Общая длительность — примерно 6 секунд, так как каждая загрузка выполняется последовательно.


Асинхронный пример

Теперь перепишем тот же код асинхронно:

import asyncio

async def download_data(name):
    print(f"Начинаю загрузку {name}")
    await asyncio.sleep(2)  # асинхронная "задержка"
    print(f"Загрузка {name} завершена")

async def main():
    await asyncio.gather(
        download_data("файл 1"),
        download_data("файл 2"),
        download_data("файл 3")
    )

if __name__ == "__main__":
    asyncio.run(main())

Результат:

Начинаю загрузку файл 1
Начинаю загрузку файл 2
Начинаю загрузку файл 3
Загрузка файл 1 завершена
Загрузка файл 2 завершена
Загрузка файл 3 завершена

Теперь общее время — около 2 секунд! Все три задачи выполняются «параллельно» в рамках одного потока.

Что происходит простыми словами:

Представьте, что вы дали трём работникам по заданию:

  1. Запуск: Программа запускает главного менеджера (asyncio.run).
  2. Раздача задач: Менеджер (main) говорит: «Вот три задачи (download_data), начните их выполнять одновременно!» (asyncio.gather).
  3. Начало работы: Каждый из трёх работников сразу же говорит «Начинаю загрузку» и ставит таймер на 2 секунды (asyncio.sleep).
  4. Ожидание: Вместо того чтобы ждать каждого по очереди, все три работника ждут свои 2 секунды одновременно.
  5. Завершение: Через 2 секунды таймеры у всех троих срабатывают, и каждый сообщает: «Загрузка завершена».

В итоге, вся работа занимает чуть больше 2 секунд, а не 6, потому что ожидание происходило параллельно.

Асинхронность и ввод-вывод (I/O)

Самое распространённое применение — сетевые запросы.

Рассмотрим пример с библиотекой aiohttp, которая позволяет делать HTTP-запросы асинхронно:

import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            print(f"{url}: {response.status}")

async def main():
    urls = [
        "https://python.org",
        "https://github.com",
        "https://timeweb.cloud"
    ]
    tasks = [fetch(url) for url in urls]
    await asyncio.gather(*tasks)

asyncio.run(main())

Этот Python-скрипт асинхронно (практически одновременно) отправляет HTTP-запросы к трём веб-сайтам: python.orggithub.com и timeweb.cloud. В результате вы увидите статусы всех трёх сайтов почти мгновенно, так как программа не ждёт завершения одного запроса, чтобы начать следующий. Это делает код в разы быстрее, чем при использовании requests.

Асинхронные библиотеки

Для работы с асинхронностью существует множество библиотек, адаптированных под asyncio:

БиблиотекаНазначение
aiohttpАсинхронные HTTP-клиенты и серверы
aiogramАсинхронные Telegram-боты
asyncpgРабота с PostgreSQL
aiosqliteРабота с SQLite
FastAPIВеб-фреймворк, построенный на асинхронности

Асинхронность уже стала стандартом для современных Python-веб-фреймворков (FastAPI, Sanic, Aiohttp Web Server).

Асинхронность ≠ параллельность

Важно не путать два понятия:

АсинхронностьПараллельность
Управление задачами в одном потокеВыполнение задач на разных ядрах процессора
Переключение между операциямиОдновременная работа нескольких потоков
Подходит для I/O-операцийПодходит для вычислительных задач

Асинхронность делает программу эффективной, но не ускоряет вычисления.
Если нужно распараллелить тяжелые вычисления (например, матрицы, машинное обучение), используют многопроцессорность через модуль multiprocessing.

Частые ошибки начинающих

  1. ❌ Использование await вне async-функции:
   await some_task()  # SyntaxError

✅ Правильно:

   async def main():
       await some_task()
  1. ❌ Смешивание asyncio и time.sleep()
    time.sleep() блокирует поток, а значит, «ломает» асинхронность. ✅ Используйте await asyncio.sleep().
  2. ❌ Отсутствие await при вызове асинхронной функции:
   async def greet():
       print("Привет!")

   greet()  # ничего не происходит

✅ Нужно:

   await greet()

Когда использовать асинхронность

Асинхронность оправдана, если:

  • Программа делает много запросов к API или БД.
  • Нужно обрабатывать несколько сетевых подключений.
  • Важно не блокировать интерфейс (например, в чат-боте).
  • Веб-сервер должен обслуживать сотни клиентов одновременно.

Но если у вас простая программа, работающая последовательно, добавлять async ради моды — не стоит. Асинхронный код сложнее для отладки и тестирования.

Когда асинхронность не нужна

Асинхронность не всегда улучшает производительность. Если программа:

  • выполняет чисто вычислительные задачи (например, обработку чисел);
  • не использует ввод-вывод (I/O);
  • работает с небольшими скриптами, где задержки не заметны — тогда проще использовать обычный синхронный код. Асинхронность полезна, когда вы ждёте много откликов из внешних источников.

Заключение

Асинхронность — это способ писать программы, которые не тратят время впустую в ожидании.
Python предоставляет мощные инструменты для асинхронного программирования: async, await, asyncio, а также экосистему библиотек (aiohttp, FastAPI, aiogram и другие).

Главное — понимать идею:

Асинхронность не делает ваш код быстрее сама по себе, она делает программу эффективнее при ожидании внешних операций.

Сначала это может показаться сложным, но если начать с простых примеров — всё становится логичным и даже удобным.

Если вы создаёте веб-приложения, ботов, парсеры или любые программы, работающие с сетью — изучение асинхронности в Python станет отличным шагом вперёд.

Хостинг для ваших проектов