Как работает механизм async/await в Python

Ответ

Механизм async/await в Python реализует кооперативную многозадачность с помощью корутин и цикла событий (event loop).

Ключевая идея — не блокировать основной поток на время ожидания операций ввода-вывода (I/O-bound tasks), а переключаться на выполнение других корутин.

Основные компоненты:

  1. Корутина (async def): Функция, объявленная с async def, при вызове возвращает объект корутины, а не выполняется немедленно. Она может приостанавливать свое выполнение.

  2. Оператор await: Приостанавливает выполнение текущей корутины, передавая управление обратно циклу событий. Цикл событий может запустить другую задачу. Когда ожидаемая операция (например, сетевой запрос) завершится, цикл возобновит выполнение приостановленной корутины.

  3. Цикл событий (Event Loop): Это ядро asyncio. Он управляет корутинами, отслеживает их состояние (готовы к выполнению, ожидают I/O) и переключает контекст между ними.

Пример работы:

import asyncio
import time

async def fetch_data():
    print("Начало загрузки данных...")
    # Имитация долгой I/O операции (например, запрос к API)
    await asyncio.sleep(2)
    print("Данные загружены.")
    return {"data": 123}

async def process_something_else():
    print("Начало другой задачи...")
    await asyncio.sleep(1)
    print("Другая задача завершена.")

async def main():
    start_time = time.time()
    # Запускаем обе корутины как задачи, чтобы они выполнялись конкурентно
    task1 = asyncio.create_task(fetch_data())
    task2 = asyncio.create_task(process_something_else())

    await task1
    await task2

    print(f"Все задачи выполнены за {time.time() - start_time:.2f} секунд")

# Запускает event loop и выполняет корутину main
asyncio.run(main())

# Вывод покажет, что общее время выполнения ~2 секунды, а не 3 (2+1),
# так как задачи выполнялись конкурентно.

Ответ 18+ 🔞

А, слушай, смотри, вот эта вся хуйня с async/await в Питоне — это, блядь, такая хитроумная жопа, чтобы не тормозить весь поток, пока ты ждёшь, когда там сервер тебе ответит, как будто ты в очереди за хлебом стоишь.

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

А тут, блядь, подъезжает кооперативная многозадачность. Это как будто у тебя в голове не один, а сразу несколько этих самых «мужиков»-корутин, и они, сука, между собой договариваются: «Я, говорит, пока буду ждать ответа от сервера, отойду, а ты, браток, делай своё дело». И управляет этим всем цирком, блядь, цикл событий (event loop), как главный режиссёр в этом ёбаном театре.

Вот из чего это говно состоит:

  1. Корутина (async def): Это не просто функция, это, блядь, такая хитрая функция-обещание. Ты её вызываешь, а она тебе не результат сразу суёт, а такой объект корутины возвращает, типа «подожди, ща всё будет». Она умеет, сука, на паузу вставать и потом с того же места продолжить.
  2. Оператор await: Это, блядь, волшебная кнопка «пауза». Когда корутина до него доходит, она такая: «О, я тут подожду, пока этот сетевой запрос выполнится». И передаёт управление обратно циклу событий. А тот, недолго думая, запускает другую готовую корутину. Когда запрос таки приполз, цикл событий возвращается и говорит: «Ну что, отдохнул? Продолжаем!».
  3. Цикл событий (Event Loop): Это, блядь, самый главный диспетчер. Он следит, кто из корутин чего ждёт, кто готов работать, и переключает их, как заёбанный светофор. Без него — нихуя не работает.

Смотри, как это в коде выглядит, блядь:

import asyncio
import time

async def fetch_data():
    print("Начало загрузки данных...")
    # Прикинься, что тут запрос к какому-нибудь API, который тормозит, как черепаха в сиропе
    await asyncio.sleep(2)
    print("Данные загружены.")
    return {"data": 123}

async def process_something_else():
    print("Начало другой задачи...")
    await asyncio.sleep(1)
    print("Другая задача завершена.")

async def main():
    start_time = time.time()
    # Создаём две задачи — пусть работают, сука, параллельно!
    task1 = asyncio.create_task(fetch_data())
    task2 = asyncio.create_task(process_something_else())

    await task1
    await task2

    print(f"Все задачи выполнены за {time.time() - start_time:.2f} секунд")

# Запускаем весь этот цирк с конями
asyncio.run(main())

# И вуаля, блядь! Время выполнения будет не 3 секунды (2+1), а всего около 2!
# Потому что пока одна задача спала, вторая тоже своё дело делала. Красота, ёпта!

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