Kotlin协程:异步执行与同步获取

一.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。

1.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类型的对象。

2.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方法

1.awaitInternal方法

    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方法启动的协程的父协程的取消回调。

3.ResumeAwaitOnCompletion类

    如果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方法启动的协程的执行结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值