资讯详情

async 函数结果获取机制分析

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

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑