K 的一隅

Python Python 语言核心

未来结果的占位符:Future 状态如何落定

Future 表示尚未完成的一次结果;pending 到 done 的状态变化、谁 set_result、回调何时执行;Task 作为挂着协程的 Future。

23 分钟阅读 更新于 2026-07-28
本文目录

HTTP 客户端把请求发出去之后,字节还没进内存——这一刻程序需要一种「占位」:将来 要么拿到响应体,要么拿到超时异常。同步代码里你 阻塞recv 上;asyncio 里则往往先拿到 Future,在 pending 阶段继续 调度 别的 Task,等生产者 set_result 后再恢复等待方。把 Future 当成状态机读,而不是当成「某个类的实例」,后面 Taskgather、取消都会顺很多。

Future 是什么:一次结果的信箱

Future 不是协程,也不是线程。它是 事件循环 体系里的 状态容器,表示 恰好一次 的结果承诺:

状态含义
pending结果尚未写入,可 add_done_callback,可 cancel()
cancelledcancel(),await 时抛 CancelledError
done已有 结果异常,不可再改
python
import asyncio


async def demo_future() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    print(fut.done(), fut.cancelled())  # False False

    fut.set_result(42)
    print(fut.done(), fut.result())     # True 42


asyncio.run(demo_future())

create_future() 得到空信箱; 持有 Future 引用, 在 I/O 完成或计算结束时调用 set_result / set_exception。消费方用 await futfut.add_done_callback 等待落定。

若对 已 doneFutureset_result,会 InvalidStateError——状态机只允许 pending → done 一次。这个约束保证:多个等待者 读到的要么是同一份结果,要么是同一份异常,不会「后写覆盖先写」。

pending → done:结果与异常两条路

完成只有两种合法落盘:

python
import asyncio


async def ok_and_err() -> None:
    ok: asyncio.Future[str] = asyncio.get_running_loop().create_future()
    bad: asyncio.Future[str] = asyncio.get_running_loop().create_future()

    ok.set_result("hello")
    bad.set_exception(ValueError("boom"))

    print(await ok)  # hello
    try:
        await bad
    except ValueError as e:
        print(repr(e))  # ValueError('boom')


asyncio.run(ok_and_err())

await 在 done 且为异常时 重新抛出 该异常;在 pending挂起当前协程,直到 Future 变 done。挂起期间 事件循环 可以跑别的 Task——这是 async 模型与「线程阻塞在 future.result()」的根本差异。

fut.result()fut.exception()同步 取值 API:在 pending 时不能调 result()(会报错);在 done 且为异常时 result() 会把异常 再抛一次。协程代码里更常用 await,由 loop 负责挂起/恢复。

重复 set_exception 或混用 set_result / set_exception 同样非法。cancel() 只在 pending 时有效:

python
import asyncio


async def cancel_demo() -> None:
    fut: asyncio.Future[None] = asyncio.get_running_loop().create_future()

    async def waiter() -> None:
        try:
            await fut
        except asyncio.CancelledError:
            print("waiter: cancelled on fut")

    t = asyncio.create_task(waiter())
    await asyncio.sleep(0)
    fut.cancel()
    await t


asyncio.run(cancel_demo())
# waiter: cancelled on fut

注意:这里 cancel 的是 Future,await 它的 Task 会以 CancelledError 结束。若 Future 先被 set_resultcancel() 无效——done 状态不可逆转。

谁 set_result:库、回调、协程

三类常见 生产者

  1. 底层 transport / protocol:socket 可读,protocol 读完后 future.set_result(data)
  2. loop.call_soon:同步步骤完成,在 callback 里写结果。
  3. Task 驱动协程:协程 return 或抛错,Task 内部等价于 set_result / set_exception

模拟「异步读一行」——生产者不在协程里,而在 I/O 完成回调:

python
import asyncio
from typing import Callable


def fake_read_line(on_done: Callable[[str], None]) -> None:
    on_done("line-1\n")


async def read_line_async() -> str:
    loop = asyncio.get_running_loop()
    fut: asyncio.Future[str] = loop.create_future()

    def _finish(line: str) -> None:
        if not fut.done():
            fut.set_result(line)

    loop.call_soon(lambda: fake_read_line(_finish))
    return await fut


async def main() -> None:
    line = await read_line_async()
    print(repr(line))  # 'line-1\n'


asyncio.run(main())

真实库里你很少手写 create_future,但 语义相同:注册 I/O → 完成时 写 Future → 等待方 await Futureasyncio.sleep 内部也是 Future + 定时器create_task 则是 Task 型 Future + 协程推进器

多个等待者:同一个 Future

一个 Future 可被多个协程 await(或多次 add_done_callback)。set_result 一次,所有等待者一起被唤醒,拿到相同结果:

python
import asyncio


async def shared_wait(tag: str, fut: asyncio.Future[int]) -> None:
    v = await fut
    print(f"{tag} got {v}")


async def main() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    asyncio.create_task(shared_wait("A", fut))
    asyncio.create_task(shared_wait("B", fut))
    await asyncio.sleep(0)
    fut.set_result(100)
    await asyncio.sleep(0)


asyncio.run(main())
# A got 100
# B got 100

这像广播完成信号,而不是每个消费者各有一个信箱。设计 API 时要想清楚:你需要的是「共享一次结果」还是「每个调用独立 Future」。

add_done_callback:回调何时跑

Future.add_done_callback(fn) 约定:fn(fut)变为 done 的同一轮 loop 里、通常 稍后 执行——在 set_result 那条调用栈 unwind 之后,由 事件循环 排进 就绪队列

python
import asyncio


async def callback_timing() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    log: list[str] = []

    def on_done(f: asyncio.Future[int]) -> None:
        log.append(f"cb result={f.result()}")

    fut.add_done_callback(on_done)
    log.append("before set")
    fut.set_result(7)
    log.append("after set")
    await asyncio.sleep(0)
    print(log)


asyncio.run(callback_timing())
# ['before set', 'after set', 'cb result=7']

after set 先于 cb——callback 不是set_result 内部同步跑。若在 callback 里写慢逻辑,会 阻塞 调度;若需要 await,应 asyncio.create_task 包一层协程。

已 doneFuture 上注册 callback,会 立即 调度一次——便于「晚到的监听者」不会漏事件。

链式 callback 与错误传播

callback 里若再 set_result 到另一个 Future,可以手工拼管道;更常见的是协程里 await 链。callback 若 抛异常,默认只打印到 stderr(3.8+ 行为有过调整),不会 自动传给原 Task——这是 callback 与 await 的重要区别:await 把异常接进协程栈;裸 callback 要自己 try/except。

python
import asyncio


async def watch() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()

    def boom(_: asyncio.Future[int]) -> None:
        raise RuntimeError("in callback")

    fut.add_done_callback(boom)
    fut.set_result(1)
    await asyncio.sleep(0)  # 给 loop 跑 callback 的机会


asyncio.run(watch())
# RuntimeError: in callback (具体表现依版本与 loop 配置)

生产代码里:短 callback 做记账/唤醒;复杂逻辑放回 Task

Task:挂着协程的 Future

asyncio.Task 继承 Future,多绑一个 协程对象。创建 Task 后,loop 驱动 协程直到 return 或未捕获异常,然后把返回值/异常写入 Future 状态。

python
import asyncio


async def compute(n: int) -> int:
    await asyncio.sleep(0.01)
    return n * n


async def main() -> None:
    task: asyncio.Task[int] = asyncio.create_task(compute(6))
    print(isinstance(task, asyncio.Future))  # True
    print(task.done())                       # False
    v = await task
    print(v, task.done())                    # 36 True


asyncio.run(main())

直觉:Future = 「结果坑位」;Task = 「坑位 + 自动跑协程填坑」。create_task(coro)Task 并 schedule 第一步。协程 return 36 在实现层等价于对该 Task set_result(36)

协程 创建 Task 就不会自己跑:

python
import asyncio


async def idle() -> str:
    return "x"


coro = idle()
print(asyncio.iscoroutine(coro))  # True
coro.close()

Task 与裸 Future 的另一个区别:Task 可被 cancel() 注入 CancelledError 打断协程;裸 Future 通常由外部 I/O 完成 set_result,取消语义更简单。

Task 名、例外与 exception()

Task 支持 get_name() / set_name(),便于日志里区分并发单元:

python
import asyncio


async def fail() -> None:
    raise KeyError("missing")


async def main() -> None:
    t = asyncio.create_task(fail(), name="loader")
    try:
        await t
    except KeyError:
        print(t.name(), t.done(), t.exception())  # loader True KeyError(...)


asyncio.run(main())

t.exception()done 且失败时返回异常对象;成功时返回 None。这与 await 再 try/except 等价,适合「已经 done、只想探测」的 调度 代码。

Future 与 concurrent.futures 的同名类

标准库还有 concurrent.futures.Future(线程/进程池)。名字一样,类型不同

python
import asyncio
from concurrent.futures import ThreadPoolExecutor


async def bridge() -> int:
    loop = asyncio.get_running_loop()
    with ThreadPoolExecutor() as pool:
        af = loop.run_in_executor(pool, pow, 2, 8)
    return await af


print(asyncio.run(bridge()))  # 256

run_in_executor 返回 asyncio.Future,内部桥接线程池里的 concurrent.futures.Future。不能 await 线程池 Future 本身——类型不对,loop 不知道何时挂起协程。

与上一章:Future 在 loop 里的位置

回顾 事件循环 的一轮:某 Task await 一个 pendingFuture → 该 Task 移出 就绪队列 → I/O 或定时器完成 → 生产者 set_resultFuture 的 done callback 与等待 Task 一起进 就绪队列 → loop 调度 恢复。整条链的枢纽就是 Future 状态从 pending 到 done 的那一次跃迁。

常见误解

误解一:「await 会阻塞线程。」 await 挂起协程,线程仍可 调度 其他 Task

误解二:「Future 就是 Promise,和 JS 一模一样。」 概念相近,但 Python 的 FutureTask 拆分、与 loop 绑定更紧。

误解三:「Task.cancel() 立刻停协程。」 cancel 在 下一个 await 才注入 CancelledErrorfinally 仍会跑。

误解四:「done_callback 里可以写阻塞 I/O。」 会冻住 事件循环;应 create_task 或线程池。

误解五:「set_result 可以从任意线程调。」 应对 loop 线程 call_soon_threadsafe 包装 set_result,否则数据竞争。

ensure_future 与 asyncio.wrap_future

库代码里常见 asyncio.ensure_future(obj):若 obj 已是 Task/Future 则原样返回;若是 协程create_task。这是「我不知道调用方给的是 coroutine 还是 Future,但我要 调度 它」的入口。

python
import asyncio


async def coro() -> int:
    return 1


async def main() -> None:
    f1 = asyncio.ensure_future(coro())
    f2 = asyncio.ensure_future(f1)
    print(f1 is f2)  # True
    print(await f2)  # 1


asyncio.run(main())

asyncio.wrap_futureconcurrent.futures.Future 包成 asyncio.Future,与 run_in_executor 内部路径类似。自己接第三方线程池时常用到。

逐步推演:从 await 到 set_result

把上一章 事件循环Future 连起来读一遍(单 Task 等手工 Future):

python
import asyncio


async def waiter() -> int:
    loop = asyncio.get_running_loop()
    fut: asyncio.Future[int] = loop.create_future()

    async def producer() -> None:
        await asyncio.sleep(0.05)
        fut.set_result(200)

    asyncio.create_task(producer())
    print("waiter: before await")
    v = await fut
    print("waiter: after await", v)
    return v


asyncio.run(waiter())

文字时间线:

  1. waiter 创建 pendingFuture启动 producer Task
  2. waiter 打印 before await,执行 await fut协程 暂停Future 登记等待者。
  3. 事件循环 调度 producer → producer await sleep → producer 暂停,loop poll 定时器。
  4. 0.05s 后 producer 恢复fut.set_result(200)Future donewaiter就绪队列
  5. waiter 恢复,打印 after await 200

全程单线程,没有 OS 级 阻塞await 上;调度 在步骤 2–5 间穿插 producer 与 waiter暂停/恢复

手工拼 Future:双阶段 I/O

再看一个稍长的 pending 阶段:先连接,再读 body——两阶段各一个 Future,由同一 协程 顺序 await

python
import asyncio
from typing import Callable


def connect(host: str, on_connected: Callable[[], None]) -> None:
    on_connected()


def read_body(on_data: Callable[[bytes], None]) -> None:
    on_data(b"OK")


async def fetch() -> bytes:
    loop = asyncio.get_running_loop()
    conn_fut: asyncio.Future[None] = loop.create_future()
    body_fut: asyncio.Future[bytes] = loop.create_future()

    def on_conn() -> None:
        if not conn_fut.done():
            conn_fut.set_result(None)

    def on_body(data: bytes) -> None:
        if not body_fut.done():
            body_fut.set_result(data)

    loop.call_soon(lambda: connect("example", on_conn))
    await conn_fut
    loop.call_soon(lambda: read_body(on_body))
    return await body_fut


async def main() -> None:
    print(await fetch())


asyncio.run(main())
# b'OK'

第一阶段 await conn_fut协程 暂停on_conn set_result 后恢复,再进入第二阶段。真实 HTTP 客户端把 socket 读就绪解析 拆成更多 Future,但状态跃迁仍是 pending → done

Task 与 Future 的 cancel 差异(再强调)

Task cancel():向 协程 注入 CancelledError对裸 Future cancel():仅标记 cancelled,没有 协程 帧要清理。若 FutureawaitTask,cancel Future 会让 TaskCancelledError 结束。混用 cancel 目标时 trace 容易乱——优先 cancel Task(用户可见的 调度 单元)。

Future 状态机(文字版)

        create_future()


          pending ──cancel()──► cancelled

              ├── set_result(v) ──► done (result=v)

              └── set_exception(e) ──► done (exception=e)

pending 时可 add_done_callback、可 cancel()doneresult() / exception() 只读;awaitpending 挂起 协程,在 done 取值或抛异常。Task 继承这套状态,额外多 协程 驱动器自动 set_result

两个 Task 等同一 Future(竞态直觉)

python
import asyncio


async def waiter(tag: str, fut: asyncio.Future[int]) -> None:
    print(f"{tag} waiting")
    print(f"{tag} got", await fut)


async def main() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    asyncio.create_task(waiter("X", fut))
    asyncio.create_task(waiter("Y", fut))
    await asyncio.sleep(0)
    print("setting result")
    fut.set_result(88)
    await asyncio.sleep(0)


asyncio.run(main())

**set_result** 一次,XY 都拿到 88。pending 阶段登记的所有等待者一起被唤醒——不会出现「第二个 await 永远等不到」除非 never set_result

为何 callback 不能 await

done_callback 在 loop 线程同步执行(从 ready 队列角度)。若 callback 里 await,当前并不是 协程 帧——语法也不允许。模式:callback 里 loop.create_task(coro()) 启动新 协程await 慢路径。

Task 创建即 schedule:与裸 coroutine 对比

python
import asyncio


async def quick() -> None:
    print("quick runs")


async def main() -> None:
    c = quick()
    print("coroutine only, not scheduled yet")
    t = asyncio.create_task(quick())
    await asyncio.sleep(0)
    c.close()


asyncio.run(main())

第一个 quick() 不打印 quick runscreate_task(quick()) 会。Task = Future + 「我已经在 事件循环 排队了」。

同步等待 vs await Future:线程视角对照

在线程池代码里,你 submit(fn) 拿到 concurrent.futures.Future,在 f.result()阻塞 直到 done。asyncio 里 await asyncio.Future 挂起 协程事件循环 线程去跑别的 Task——这是 async 高并发的支点。不要把 await fut 想成「睡直到有结果」;应想成「把本 协程 登记在 fut 的等待列表上,把 控制权 交还 调度」。

result()、exception() 与 await 的分工

fut.result() 是同步 API:pending 调会错;done 且异常时会 再抛。在 async 函数里优先 await fut,由 loop 负责 挂起/恢复。在 同步 callbackadd_done_callback)里不能 await,只能 fut.result()fut.exception(),且应快速返回。

python
import asyncio


async def sync_style_bad() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    asyncio.get_running_loop().call_later(0.05, fut.set_result, 9)
    # fut.result()  # 在 async 里阻塞 loop,错误
    print(await fut)


asyncio.run(sync_style_bad())
# 9

延迟 set_result 与取消 race

pending 期间 cancel()set_result 竞态:cancel 成功后 set_resultInvalidStateError;若 set_result 先完成,cancel 无效。写库时应用 if not fut.done(): fut.set_result(...) 模式(见前文 fake_read_line)。

python
import asyncio


async def race() -> None:
    fut: asyncio.Future[int] = asyncio.get_running_loop().create_future()
    fut.cancel()
    try:
        fut.set_result(1)
    except asyncio.InvalidStateError:
        print("lost race to cancel")


asyncio.run(race())
# lost race to cancel

Task 的 get_coro 与调试

task.get_coro()(3.8+)取绑定的 协程 对象,配合栈追踪看 Task 卡在哪个 await。生产日志里打印 task.get_name()repr(task) 比打印裸 Future 更可读——Task 是运维与 调度 的常用粒度。

Future 链:callback 触发下一级 I/O

复杂客户端常级联:连接 Future done → callback 发起 读 Future读 done set_result 给上层 await。每一跳都是 pending → done事件循环 在跳与跳之间 调度 其它 Task。理解 Future 链后,读 aiohttp / httpx 源码不会迷失在 callback 海里。

再次对照 Task

FutureTask
谁推进 pending外部 set_resultloop 跑 协程 自动 set
典型来源create_future、I/O 层create_task(coro)
cancel 语义标记 cancelled标记 + 向 协程 注入 CancelledError
await等同 Future等同 Future(继承)

TaskFuture;不是所有 Future 都是 Task

包装线程结果:wrap_future 走读

python
import asyncio
from concurrent.futures import ThreadPoolExecutor


async def main() -> None:
    loop = asyncio.get_running_loop()
    with ThreadPoolExecutor() as ex:
        cf = ex.submit(pow, 2, 16)
        af = asyncio.wrap_future(cf)
        print(await af)


asyncio.run(main())
# 65536

wrap_future 监听线程池 Future done,在 loop 线程 set_resultasyncio.Future——与 run_in_executor 同类桥接。

await 与 Future 在字节码层的协作(概念)

await fut 编译为:检查 fut 是否已 done → 若 pending,挂起当前 协程 并把 fut 登记为 awaitable事件循环fut.set_result 时恢复 协程。因此 set_result 的调用线程必须是 loop 线程,或通过 call_soon_threadsafe 投递——否则 等待者生产者 数据竞争。

多个 Future 顺序 await:仍无并发

python
import asyncio


async def one() -> asyncio.Future[int]:
    f = asyncio.get_running_loop().create_future()
    asyncio.get_running_loop().call_later(0.05, f.set_result, 1)
    return f


async def main() -> None:
    f1 = await one()
    f2 = await one()
    print(await f1, await f2)


asyncio.run(main())

**await one()** 先等第一个 Future 完成再调第二个 one()——总耗时约 0.1s。并发要 create_task(one()) 两次再 gather

Task.exception() 与日志

Task donetask.exception() 取异常而不 re-raise,适合 add_done_callback 里记录失败:

python
import asyncio


async def fail() -> None:
    raise RuntimeError("x")


def log_task(t: asyncio.Task[None]) -> None:
    if t.cancelled():
        return
    exc = t.exception()
    if exc:
        print("logged", exc)


async def main() -> None:
    t = asyncio.create_task(fail())
    t.add_done_callback(log_task)
    try:
        await t
    except RuntimeError:
        pass


asyncio.run(main())
# logged x

Future 不是缓存容器

Future 表示 一次 结果。done 后不可 set_result again。若需要多次推送,用 Queueasync generator回调流——不要复用同一个 Future

事件循环 就绪队列的最后一环

set_resultFuture 标记 done → 等待该 FutureTask 被 loop call_soon就绪队列 → 下一轮 调度 恢复 协程。记牢这条链,pending 卡住时问:谁该 set_result?是否已 call_soon

重复读取 done 的 Future

await fut 多次、或 fut.result() 多次,在 done 且成功时返回 同一结果——Future单次广播。异常路径 await 每次 re-raise 同一异常对象(依实现可能新建 traceback)。

asyncio.gather 与 Future 列表

gather 返回列表,元素是各 awaitable 的结果,不是 Future 本身——除非某元素本身就是 Futurereturn_exceptions 等。理解 gather 输出与 Task 对象的区别,避免 **gather 完再 await task 双重等待。

超时 Future:wait_for 实现直觉

wait_for 内部 ensure_future(aw),并注册 TimerHandle;超时 cancel 底层 Taskawait 方捕 TimeoutError。底层 Task吞 cancelwait_for 仍返回超时,但 Task 可能仍在跑——须 await 或显式 cancel 确认。

手写 Future 的最小模式(模板)

python
import asyncio
from collections.abc import Callable


def run_async(fn: Callable[[asyncio.Future], None]) -> asyncio.Future:
    loop = asyncio.get_running_loop()
    fut = loop.create_future()

    def _safe_set(f: asyncio.Future, result=None, exc=None):
        if f.done():
            return
        if exc:
            f.set_exception(exc)
        else:
            f.set_result(result)

    try:
        fn(fut)
    except Exception as e:
        _safe_set(fut, exc=e)
    return fut

库作者用类似模式把 callback 风格 API 包成 await 友好 Future

Task 命名与可观测性

生产环境给 Task set_name(f"http-{host}"),日志 asyncio.all_tasks() 列出 pending Task,配合 Future done 统计,能回答「请求 hung 在哪个 await」——Future 状态机是观测 hung 的锚点。

协程 章:await

协程 await futfut pending协程 暂停fut done 则取值。Task自驱动 Future协程 returnset_result)。读 Task 栈 + Future done 位,定位 pending 链。

深入:Future 在 asyncio 模块中的位置

标准库把 Future 放在 asyncio 包核心:Task 继承 Futuregatherwaitwait_for 都操作 Awaitable,底层大量 ensure_future。第三方库(aiohttphttpx)在 protocol 层 create_futureloop.create_task,对上层仍暴露 await response.read()。读任何 async 库,找 谁持有 Future谁 set_result,比记类名列表有用。

异常链与 Future

set_exception(e) 保存 eawaitre-raise 同一对象(__context__ 链保留若 raise ... from)。Task 未捕获异常 → Task done with exception → await task 抛错。**gather(return_exceptions=True)** 把异常 对象 放进列表,不抛——适合批处理报表。

并发 set_result:线程安全

仅 loop 线程应 直接 fut.set_result。其他线程须:

python
import asyncio
import threading


async def main() -> None:
    loop = asyncio.get_running_loop()
    fut = loop.create_future()

    def worker():
        loop.call_soon_threadsafe(fut.set_result, "from-thread")

    threading.Thread(target=worker).start()
    print(await fut)


asyncio.run(main())
# from-thread

call_soon_threadsafe 保证 set_result 在 loop 线程执行,与 await无 race

长文走读:Task 完成时发生了什么

  1. 协程 执行 return 42 或抛 未捕获异常
  2. Task 内部捕获 StopIteration(42) 或异常。
  3. Task 调用 Future.set_result(42)set_exception
  4. Future 标记 done,触发 add_done_callback 队列(排进 ready)。
  5. await task协程call_soon 恢复,await 表达式求值为 42抛异常

五步都在 同一 loop 线程 完成(除 threadsafe 路径)。pending 卡住 = 第 3 步从未发生。

Future 与协议:StreamReader 直觉

asyncio StreamReader.read(n) await 时,若缓冲区空,内部 Future pending 直到 protocol feed_data set_result。你 await reader.read() 就是 await 那个 Future——语法是 协程,机制是 Future pending → done

小结补充:与下一章 asyncio 模式

Future doneTask 才可 await 取值;gather 等多 Task done。模式章的 create_task 启动cancel 打断,都假设你认 Future 状态机——pendingcancel 有效,done 后只能读结果。

附录式走读:从零实现「延迟返回值」

假设没有 asyncio.sleep,只有 loop.call_laterFuture,你可以手写最小 sleep

python
import asyncio


async def my_sleep(delay: float) -> None:
    loop = asyncio.get_running_loop()
    fut = loop.create_future()
    loop.call_later(delay, fut.set_result, None)
    await fut


async def main() -> None:
    print("t0")
    await my_sleep(0.05)
    print("t1")


asyncio.run(main())

my_sleepFuturependingcall_later set_result(None)await fut协程 暂停。这就是 Future 作为 时间结果 桥梁的原型——标准库 sleep 只是更完整版本(含 cancel、精度处理)。

为何 Task 必须是 Future 子类

调度器 需要统一接口:awaitdone_callbackcancelexception()。若 TaskFuture 分裂,gather 要写两套逻辑。继承Task 可直接 await,且 isinstance(t, asyncio.Future) 为真——统一 pending 语义。

读取 pending 状态的工具

fut.done()fut.cancelled()fut.exception()done 且失败时)可在 add_done_callback 或断言里用。pendingfut.done()False——简单但有效。调试 hung:all_tasks 里找 Task,对每个 awaitFuture 是否 set_result

Future 与 CancelledError 的特殊性

cancelled Future awaitCancelledErrorBaseException 子类,许多 except Exception 捕不到)。业务 cleanupfinally框架CancelledError 后 often re-raiseTask.cancelFuture.cancel 都走这条语义链。

与 gather:多个 Future 同时 pending

gather(t1, t2) 内部 ensure_future,单 协程 await全部 done。每个 Task 各自 Future 状态机;gather 只是 汇合层。任一 Task exception 默认 gather 失败——理解 Future done with exception 如何冒泡。

再谈 set_result 的责任边界

在 I/O 回调 set_result应用 少手写 set_result,多 await 库返回的 Future/协程应用 手写 create_future 常见于:适配 callback API线程 call_soon_threadsafeloop单元测试 mock I/O。责任边界 清晰可减少 pending 永不 done 的 hung。

状态机记忆卡片

  • pending + await协程 挂起
  • pending + cancelcancelled
  • done + resultawait 得值
  • done + exceptionawait
  • Task = Future + 协程 驱动

卡片贴显示器旁,读 traceback 时对照 Future 状态,比翻文档快。

读者自查:Future 概念是否真懂

合上文章,口头回答:pendingawait协程 去哪了?set_result 后谁先进 readyadd_done_callbackawait 谁先谁后?Task 与裸 Future cancel 差在哪?concurrent.futures.Future 为何不能直接 await?答不出就回看对应小节。

与事件循环章的交叉索引

事件循环 维护 readyFuture set_result 把等待 Task 推进 readyadd_done_callbackfn 也进 readyFutureloop协程 之间的 状态枢纽——两章应交叉阅读。

生产故障模式:Future 永不 done

hung 请求多数是:Future 永远 pending(对端未回包、忘记 set_resultcallbackcall_soon_threadsafe死锁sync Lock)。排查:Task 栈 + Future done 位 + 是否 loop 阻塞。不是 async 魔法失灵,是 状态机 未闭合。

扩展阅读方向(概念,非书单)

PEP 492 理解 async/await 语法;读 asyncio 文档 FutureTask 节对照源码 Tasks/futures.py;读 Tr asyncio 源码 Future.await。方向对了,API 增删不必从头学。

最后一轮机制巩固:Future 与 I/O 回调伪代码

伪代码表达 socket 可读时 Future 如何 done(非真实源码,助理解):

on_socket_readable(fd):
    data = os.read(fd, bufsize)
    fut = fd_to_future[fd]
    if not fut.done():
        fut.set_result(data)

协程 await read_futurepending 挂起;on_socket_readable 在 loop 线程(或 threadsafe 投递后)set_result协程 下一 ready恢复Task 路径则是 loop 直接 驱动 协程await read_future,之后同上。

为何 asyncio 坚持 Future 单次结果

多次 set_result 会破坏 await 语义:await 期望 一次 结果。流式数据用 async generatorQueue.put 多次推送——不同 协议。混淆「单次 Future」与「流」会导致 API 设计灾难。

Task 与 Future 在 typing 中的写法

asyncio.Task[int]asyncio.Future[bytes] 3.9+ 泛型标注帮助静态检查;运行时仍同一状态机。pending 不是类型参数——是 运行时 位。

合并阅读建议

先读 事件循环调度 从哪来),再读 Futurepending 如何 done),再读 协程await 如何 挂起),最后读 模式create_task/gather)。Future 章是中间枢纽:向上接 loopready,向下接 协程awaitset_result 一词贯穿 I/O 库与 Task 完成——抓住 状态机 就不怕 API 表面杂。

手写测试:Future 是否 done

python
import asyncio


async def test_future_done() -> None:
    f = asyncio.get_running_loop().create_future()
    assert not f.done()
    f.set_result(0)
    assert f.done()
    assert f.result() == 0


asyncio.run(test_future_done())

三行断言固化 pending → done——比背定义更接近 运行时 真相。

Future 与 async/await 的历史位置

async/await 语法(PEP 492)让 Future pending协程 挂起 在源码里可读;底层仍是一次次 set_resultloop call_soonFuture 概念在 TwistedTornado 时代已有;asyncio 把它与 Taskloop 收成标准库一体。学 Future 状态机等于学 Python 3 官方 异步 模型的 ** lingua franca**。

结束语前的检查表

  • 能解释 pending / done / cancelled
  • 能说出 set_result 典型调用者
  • 能区分 Task 与裸 Future
  • 能说明 add_done_callback 何时跑
  • await run_in_executor 返回的 asyncio.Future

五项打勾,Future 章可以收工。

深度:await Future 时 CPython 大致做什么

await fut 触发 fut.__await__(),返回 iteratorTask 驱动该 iterator 直到 StopIteration 携带结果。pendingiterator yield 控制权,Task 登记 Futureset_resultTask send 恢复 iteratorawait 表达式完成。不必读 C 源码,但应知 await 不是 syscall,是 协程 协议 + Future 状态联动。

与第三方 Event Loop

asyncio 也可 plug uvloop 等(第三方 loop 实现),Future/Task 语义不变——仍是 pending/doneset_resultawait事件循环 实现可换,Future 状态机是 asyncio 抽象核心。

Future 章节完整复述(一页纸)

Future 表示单次异步结果;生命周期 pending → done/cancelledset_result/set_exception 由 I/O、Taskthreadsafe 回调调用;awaitpending 挂起 协程add_done_callbackdonesoon 于 loop 执行;Task 继承 Future驱动 协程 填结果。背下这段,读 httpx/aiohttp 源码时 Future 满屏不会怵。

与 JavaScript Promise 的单向对比(一句)

Futureset 一次,await协程 里;JS Promise then概念 相近,但 Python Task/loop 绑定更紧——不要Node 事件循环经验 原样GIL + asyncio,分工边界不同(见 事件循环多线程 表)。

温故:Future 相关的四个「谁」

谁创建 Future? 常是 loop.create_futureTask 内部。谁 set_result? I/O 回调、Task 完成、call_soon_threadsafe谁 await? 业务 协程谁 add_done_callback? metrics、logging、链式 I/O。四个 答不清时,pending hung 很难查——建议每遇 hung 在日志里打这四类角色的 id/name

温故:pending 与 done 的 await 行为对照

状态await 行为
pending挂起 协程,登记等待
done 成功立即得 result,不 挂起
done 异常立即 exception
cancelled CancelledError

表适合打印贴在 async 专题笔记首页;与 Taskgather 联用时 对照 trace

收束

Future 是 asyncio 的「结果插座」:pending 等插,set_result 通电,await协程 灯亮。Task 是带自动插线的插座。整章若只记一句,记:await 的是 Future 状态,不是魔法。

Future 章节建议配合调试器在 set_result 行与 await fut 行各断点一次,亲眼看到 pendingdone 两侧 协程 栈变化——比再读十页文档更牢固。若只能记一个 API,记 create_futureset_result 这对「空信箱与填信箱」组合即可串起全章。下一章 协程 会说明 await 如何挂在这类信箱上。通读后应能用一句话向同事解释:Future 就是 loop 里的一次性结果位。

小结

Future 承载 pending → done 的一次结果或异常;set_result / set_exception 由 I/O 层、Task 或 glue 代码调用。add_done_callback 在 loop 里 稍后 触发。TaskFuture 子类,把 协程 执行与结果落盘绑在一起。下一篇进入协程本体:async defawait 如何在帧里 暂停与恢复