Что представляет собой объект Future в asyncio?

Ответ

В asyncio, Future — это низкоуровневый объект, который представляет собой результат асинхронной операции, который может быть еще не готов. Он служит для отслеживания состояния выполнения операции (ожидание, завершено, отменено) и получения её конечного результата или возникшего исключения. Концептуально Future схож с Promise в JavaScript или Future в Java.

Назначение: Future позволяет асинхронным функциям «обещать» результат, который будет доступен позже, не блокируя при этом основной поток выполнения. Это критически важно для неблокирующего ввода-вывода и параллельного выполнения задач.

Основные методы:

  • done(): Возвращает True, если Future завершен (успешно, с ошибкой или отменен).
  • result(): Возвращает результат Future. Если Future не завершен, вызывает InvalidStateError. Если завершен с исключением, выбрасывает это исключение.
  • exception(): Возвращает исключение, если Future завершился с ошибкой.
  • set_result(result): Устанавливает результат Future.
  • set_exception(exception): Устанавливает исключение для Future.
  • add_done_callback(callback): Добавляет функцию обратного вызова, которая будет вызвана, когда Future завершится.

Пример использования Future напрямую:

import asyncio

async def set_future_value(future_obj):
    """Имитирует асинхронную операцию, устанавливающую результат Future."""
    print("Начинаем асинхронную операцию...")
    await asyncio.sleep(1) # Имитация задержки
    future_obj.set_result("Данные успешно получены!")
    print("Результат установлен в Future.")

async def main():
    loop = asyncio.get_running_loop() # Получаем текущий цикл событий
    future = loop.create_future() # Создаем объект Future

    # Запускаем корутину, которая установит результат в future
    loop.create_task(set_future_value(future))

    print("Ожидаем результат Future...")
    result = await future # Ожидаем завершения Future
    print(f"Полученный результат: {result}")

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

Важное замечание: В большинстве случаев разработчики не создают Future напрямую. Вместо этого используются высокоуровневые абстракции, такие как asyncio.Task. asyncio.Task — это подкласс Future, который оборачивает корутину и планирует её выполнение в цикле событий. Когда вы используете async def функции и await, вы неявно работаете с Task и Future объектами, которые управляются asyncio.

Ответ 18+ 🔞

Слушай, а вот этот твой asyncio.Future — это такая штука, которая обещает тебе результат, но ещё нихуя не сделала. Представь, ты заказал пиццу, а тебе дали пустую коробку и сказали: «Жди, мужик, сейчас наполним». Вот эта коробка — и есть Future. Она пока пустая, но в ней может появиться либо пицца, либо, если курьер упал, — хуй с солью.

Зачем это надо, блядь? Чтобы не стоять как идиот и не пялиться в потолок, пока операция выполняется. Ты говоришь: «Окей, Future, как будешь готов — пискни». А сам в это время можешь другие дела делать, ебашить параллельно. Это основа всего асинхронного движняка — никаких блокировок, всё летает.

Что с ним можно делать, этот Future:

  • done() — глянуть, готово ли уже, или всё ещё в процессе. True — значит, пицца в коробке (или таракан, если не повезло).
  • result() — открыть коробку и достать что внутри. Если открыть раньше времени — получишь ошибку в еблет. Если внутри исключение — оно тебе в рожу выскочит.
  • exception() — специально посмотреть, не засунули ли тебе в коробку исключение вместо пиццы.
  • set_result() — вот этим методом ты сам кладёшь пиццу в коробку. Типа, «всё, готово, на, жри».
  • set_exception() — а этим — подкладываешь кирпич. «Извини, братан, не срослось».
  • add_done_callback() — повесить колбэк, который сработает, когда коробка наполнится. Типа «позвони мне, когда придёт».

Вот тебе живой пример, смотри:

import asyncio

async def set_future_value(future_obj):
    """Притворяется долгой операцией и суёт результат в Future."""
    print("Начинаем долгую хуйню...")
    await asyncio.sleep(1) # Симулируем, что мы что-то ждём
    future_obj.set_result("Вот твои данные, довольный?")
    print("Запихнул результат в Future.")

async def main():
    loop = asyncio.get_running_loop()
    future = loop.create_future() # Создаём пустую коробку-обещание

    # Запускаем задачу, которая эту коробку наполнит
    loop.create_task(set_future_value(future))

    print("Сижу, жду, когда Future созреет...")
    result = await future # Тут мы реально ждём, пока коробку наполнят
    print(f"Получил: {result}")

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

Но есть важный момент, ёпта! В нормальной жизни ты редко когда будешь вручную эти Future создавать и пихать в них результаты. Это как собирать двигатель из говна и палок — можно, но зачем, если есть завод? Высокоуровневая абстракция — asyncio.Task. Это тот же Future, но он уже обёрнут вокруг твоей корутины (async def функции) и сам всем управляет. Когда ты пишешь await some_coro(), под капотом как раз создаётся Task (наследник Future), который и крутится в цикле событий. Так что не выёбывайся с низкоуровневыми фьючами без крайней нужды.