一、总体结论
async 的返回值最终存放在 JobSupport 的 state 字段中,await() 只是去读这个字段——读到了就直接返回(快路径);读不到就挂起自己,等协程完成回调把自己唤醒后再读一次(慢路径)。结果传递的本质是「共享状态 + 完成回调」,不是消息推送。
二、逐段源码与分析
1. async() 本体——结果的写入端
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
分析:async() 本身不碰结果。coroutine.start(start, coroutine, block) 把 block 调度到新协程执行,async() 立即返回 DeferredCoroutine 实例(本质是一个 Job)。block 执行完毕时,由父类 AbstractCoroutine / JobSupport 的状态机把结果(值或异常)通过 CAS 写入 state 字段,state 从 Incomplete 变为完成态——这就是结果的写入端。
2. DeferredCoroutine 类——await 的转发层
private open class DeferredCoroutine<T>(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<T>(parentContext, active), Deferred<T>, SelectClause1<T> {
override fun getCompleted(): T = getCompletedInternal() as T
override suspend fun await(): T = awaitInternal() as T
override val onAwait: SelectClause1<T> get() = this
override fun <R> registerSelectClause1(select: SelectInstance<R>, block: suspend (T) -> R) =
registerSelectClause1Internal(select, block)
}
分析:DeferredCoroutine 自身几乎没有逻辑,await() 只是转发到父类的 awaitInternal()。它同时实现 SelectClause1,使 async 的结果还能参与 select 多路选择(onAwait)。所有核心机制都在父类 JobSupport 的状态机中。
3. awaitInternal()——快路径:一次读取直接拿结果
internal suspend fun awaitInternal(): Any? {
while (true) {
val state = this.state
if (state !is Incomplete) {
if (state is CompletedExceptionally) {
recoverAndThrow(state.cause)
}
return state.unboxState()
}
if (startInternal(state) >= 0) break
}
return awaitSuspend()
}
分析:无锁 while(true) 循环读取 state,分三种情况。一,已完成且正常:state.unboxState() 直接返回结果——一次读取即拿到结果,零挂起、零对象分配,这就是快路径。二,已完成但是 CompletedExceptionally:执行 recoverAndThrow(state.cause) 抛出异常(慢速路径用于恢复堆栈)。三,未完成:startInternal(state) >= 0 时 break,转入 awaitSuspend() 慢路径。
4. awaitSuspend()——慢路径:挂起并注册完成回调
private suspend fun awaitSuspend(): Any? = suspendCoroutineUninterceptedOrReturn { uCont ->
val cont = AwaitContinuation(uCont.intercepted(), this)
cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(this, cont).asHandler))
cont.getResult()
}
分析:这是「最后怎么拿到结果」的核心入口,共三步。第一步,suspendCoroutineUninterceptedOrReturn 捕获调用方协程的原始 continuation(uCont)并拦截分发。第二步,包装成 AwaitContinuation,并通过 invokeOnCompletion(ResumeAwaitOnCompletion(this, cont)) 在 Job 上注册完成回调——注释专门说明:这样父协程 await 子 async 协程时,抛出的是子协程的真实异常,而不是 JobCancellationException。第三步,cont.getResult() 决定挂起还是直接拿结果(见下一段)。
5. getResult()——挂起与恢复的枢纽
internal fun getResult(): Any? {
setupCancellation()
if (trySuspend()) return COROUTINE_SUSPENDED
val state = this.state
if (state is CompletedExceptionally) throw recoverStackTrace(state.cause, this)
if (resumeMode.isCancellableMode) {
val job = context[Job]
if (job != null && !job.isActive) {
val cause = job.getCancellationException()
cancelCompletedResult(state, cause)
throw recoverStackTrace(cause, this)
}
}
return getSuccessfulResult(state)
}
分析:完整时序是五步。第一步,trySuspend() 成功时返回 COROUTINE_SUSPENDED 哨兵值,await 挂起点生效,调用方协程在此让出线程。第二步,async 协程体执行完毕,state 被置为结果,触发 ResumeAwaitOnCompletion 回调,恢复等待者的 continuation。第三步,恢复后 getResult() 重新读取 state:异常态则 throw recoverStackTrace(...)。第四步,父 Job 已取消则抛出取消异常——处理「注册回调与协程完成之间的竞态」:suspendCancellableCoroutine 若在 block 返回前就 cont.resume(...),此处本会错误返回结果而非取消异常。第五步,一切正常则走 getSuccessfulResult(state)。另外 trySuspend() 失败本身也是竞态兜底:说明注册回调时协程恰好已完成,此时直接从 state 拿值返回,不挂起。
6. getSuccessfulResult()——最终取值
override fun <T> getSuccessfulResult(state: Any?): T =
when (state) {
is CompletedContinuation -> state.result as T
else -> state as T
}
分析:最终取值非常简单——state 是 CompletedContinuation 时取其 result 字段强转为 T(完成态可能同时携带异常与句柄等附加信息);否则普通值直接装箱在 state 上,强转即结果。整个链路到这里收敛:await 的返回值,就是当初 block 执行完毕时 CAS 写入 state 的那个值。
三、可强调的三个机制要点
- 结果传递 = 共享状态 + 完成回调,不是消息推送:值挂在 state 上,等待者被唤醒后自己去读。
- COROUTINE_SUSPENDED 哨兵协议:挂起函数通过返回这个特殊对象告知协程框架「我没有结果,我挂起了」,这是整个 Kotlin 挂起机制的底座。
- 快慢路径双保险:trySuspend() 失败意味着注册回调时协程恰好完成了(竞态),此时 getResult 直接从 state 拿值返回,不挂起。