一.async方法
在协程中,可以通过async方法开启一个协程,执行任务。但与launch方法不同的是,async方法会返回一个Deferred接口指向的对象,通过调用找个对象的await方法,可以获取任务的执行结果,如果这时任务没有结束,await方法还会同步挂起等待任务执行完毕返回结果再继续执行。
async方法代码如下:
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {// 返回类型Deferred
// 计算新的上下文
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方法启动协程的逻辑与launch方法是相同的,launch方法在Kotlin协程:创建、启动、挂起、恢复中分析过。async方法与launch方法唯一不同的就是返回对象的类型,launch方法返回对象的类型为Job,async方法返回对象的类型为Deferred。
接口
Deferred接口继承了Job接口,主要定义了await方法,代码如下:
// 继承Job接口
public interface Deferred<out T> : Job {
// 一个挂起方法,用于等待获取最终执行结果
public suspend fun await(): T
// 配合select方法,实现多路复用
public val onAwait: SelectClause1<T>
// 实验方法,之后可能删除或重新命名
// 用于当确定任务已经执行完毕后调用,获取最终的结果
// 如果任务未执行完毕或已经取消,则会抛出一个异常
@ExperimentalCoroutinesApi
public fun getCompleted(): T
// 实验方法,之后可能删除或重新命名
// 用于当确定任务已经执行完毕后调用
// 如果任务正常执行结束,则返回null
// 如果任务没有执行完毕,则会抛出一个异常
// 如果任务被取消,则会返回对应的异常
@ExperimentalCoroutinesApi
public fun getCompletionExceptionOrNull(): Throwable?
}
在默认启动模式下,async方法最终返回的Deferred接口指向的对象DeferredCoroutine类型的对象。
类
DeferredCoroutine继承自AbstractCoroutine类,实现了Deferred接口与SelectClause1接口,代码如下:
@Suppress("UNCHECKED_CAST")
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类内部通过调用父类JobSupport的awaitInternal方法实现了await方法。
二.await方法
方法
awaitInternal方法是await方法的核心实现,定义在JobSupport类中,代码入下:
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()
}
// 如果没有执行完成,则启动执行
// 如果返回的结果为TRUE或FALSE,即不是RETRY,则跳出循环
// 如果返回RETRY,则会再循环一次,启动执行
if (startInternal(state) >= 0) break
}
// 挂起等待结果并返回
return awaitSuspend()
}
awaitInternal方法首先会尝试获取执行结果,同时会触发协程的启动。如果任务没有执行完毕,则会通过awaitSuspend方法进入挂起状态,等待任务执行结束。
2. awaitSuspend方法
awaitSuspend方法用于挂起当前协程,等待async方法执行完毕后恢复,代码如下:
// 获取续体
private suspend fun awaitSuspend(): Any? = suspendCoroutineUninterceptedOrReturn { uCont ->
// 包装成AwaitContinuation续体
val cont = AwaitContinuation(uCont.intercepted(), this)
// 注册内部协程执行完毕的回调与外部协程取消的回调
cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(this, cont).asHandler))
// 尝试获取结果
cont.getResult()
}
awaitSuspend方法与之前在Kotlin协程:生命周期原理中提到的joinSuspend方法实现原理类似。这里的getResult方法也是之前在Kotlin协程:生命周期原理提到的CancellableContinuationImpl类的方法,首次调用会挂起协程。
这里需要注意,通常在一般情况下,我们通过async方法启动协程去执行任务,并且在async方法启动的协程的父协程中挂起等待执行结果。awaitSuspend方法中,invokeOnCompletion方法注册的是async方法启动的协程的完成回调,disposeOnCancellation注册的是async方法启动的协程的父协程的取消回调。
类
如果async方法所在协程的父协程被取消,则过程与之前分析的cancel方法的调用路径相同。
如果async方法所在协程正常的执行完毕,则根据之前的分析,会通过嵌套的调用invoke方法,最终调用到ResumeAwaitOnCompletion类对象的invoke方法,代码如下:
private class ResumeAwaitOnCompletion<T>(
job: JobSupport,
private val continuation: CancellableContinuationImpl<T>
) : JobNode<JobSupport>(job) {
override fun invoke(cause: Throwable?) {
val state = job.state
assert { state !is Incomplete }
// 如果发生取消
if (state is CompletedExceptionally) {
// 抛出异常并恢复
continuation.resumeWithException(state.cause)
} else {
// 如果正常完成,则直接恢复,并传入最终结果
@Suppress("UNCHECKED_CAST")
continuation.resume(state.unboxState() as T)
}
}
override fun toString() = "ResumeAwaitOnCompletion[$continuation]"
}
这里的continuation对象,就是通过intercepted方法调度后的async方法启动协程的父协程的续体。传入最终结果,就是async方法启动的协程的执行结果。