568 字
3 分钟
async/await 状态机

原型#

// 源码
func loadData() async throws -> String {
    let uid = try await fetchUserID()        // await-1
    let profile = try await fetchProfile(uid) // await-2
    return profile.name
}

伪 C#

⚠️ 只保留关键字段;真正产物是 LLVM IR → machine code。

// ⬢ 每个 async 函数对应一个上下文帧(放在堆上)
typedef struct {
    SwiftAsyncContext header;   // Swift 运行时代码生成的公共头
    uint8_t   state;           // 状态机标签
    Error    *error;           // 异常槽
    String    result;          // 返回值槽
    /* 持久化的局部 */
    UserID    uid;
    Profile   profile;
} LoadDataContext;

/* 编译器展开后的实现,swiftasync 调用约定 */
void loadData_swiftasync(LoadDataContext *ctx) {
  switch (ctx->state) {

    case 0: { // 初始态
      /* 将“后续步骤”封装成 continuation,传给被等待的函数 */
      ctx->state = 1;
      fetchUserID_swiftasync(/*continuation=*/ctx);
      return;                           // ⬅️ 挂起,线程可去干别的
    }

    case 1: { // await-1 恢复点
      if (ctx->error) goto fail;        // fetchUserID 报错
      ctx->uid = ctx->header.resume_val;
      ctx->state = 2;
      fetchProfile_swiftasync(ctx);     // 第二个 await
      return;
    }

    case 2: { // await-2 恢复点
      if (ctx->error) goto fail;        // fetchProfile 报错
      ctx->profile = ctx->header.resume_val;
      ctx->result = ctx->profile.name;  // 计算最终返回值
      swift_async_return(ctx);          // 触发最外层 .value
      return;
    }
  }

fail:
  swift_async_throw(ctx, ctx->error);   // 向上传播 try/catch
}

要点#

概念伪 C 中体现
挂起点return 把控制权交回任务调度器
状态机ctx->state + switch
Continuation上下文指针 ctx 自身即是 continuation
局部变量跨挂起点提升到 LoadDataContext 成员

Rust 编译器生成的 Future 状态机#

// #[allow(…)]: 仅演示
enum LoadDataFuture {               // 状态枚举
    Step0,
    Step1 { fut_uid: FetchUserID }, // 持久化局部 = 字段
    Step2 { uid: UserID, fut_prof: FetchProfile },
    Done(Result<String, Error>),
}

impl Future for LoadDataFuture {
    type Output = Result<String, Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>)
        -> Poll<Self::Output> {

        loop {
            match *self {
                LoadDataFuture::Step0 => {
                    let fut = fetch_user_id();              // 创建子 future
                    *self = LoadDataFuture::Step1 { fut_uid: fut };
                }

                LoadDataFuture::Step1 { ref mut fut_uid } => {
                    match fut_uid.poll(cx) {
                        Poll::Pending => return Poll::Pending, // 挂起
                        Poll::Ready(id) => {
                            let fut = fetch_profile(id?);
                            *self = LoadDataFuture::Step2 { uid: id?, fut_prof: fut };
                        }
                    }
                }

                LoadDataFuture::Step2 { ref mut fut_prof, .. } => {
                    match fut_prof.poll(cx) {
                        Poll::Pending => return Poll::Pending,
                        Poll::Ready(profile) => {
                            *self = LoadDataFuture::Done(Ok(profile?.name));
                            return Poll::Ready(Ok(profile?.name));
                        }
                    }
                }

                LoadDataFuture::Done(ref out) => return Poll::Ready(out.clone()),
            }
        }
    }
}

你看到的 await ➜ poll(cx) 环就是 回调 + 状态机 的另一种等价实现。

  1. 编译期 把顺序写法拆成「switch-驱动的状态机」。
  2. 运行时 用 continuation/Future::poll 把任务挂起、排队,事件就绪后再恢复。
  3. 写出来像同步,执行方式仍完全异步,靠调度器节约线程。
async/await 状态机
https://blog.lpkt.cn/posts/async-await-state-mach/
作者
lollipopkit
发布于
2025-05-20
许可协议
CC BY-NC-SA 4.0