You need to enable JavaScript to run this app. Kotlin协程中缀Builder模式实现:不同协程上下文代码链式调用
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin协程中缀Builder模式实现:不同协程上下文代码链式调用

实现调整方案

核心问题原因

原有实现的问题主要有两个:

  1. ui函数的入参类型为泛型T,当IO块返回可空类型时,T本身就是可空类型,即使做了非空判断再调用block,传递给用户的参数还是可空类型,需要手动二次校验
  2. 逻辑上没有做ui和ifNull块的互斥执行明确限制,导致需要在ui块里自己处理空分支的逻辑

调整后完整实现代码

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

@OptIn(ExperimentalCoroutinesApi::class)
class CoroutineLinker<T> (
    private val value: T?,
    private val scope: CoroutineScope
) {

    suspend infix fun <K> io(block: suspend () -> K?): CoroutineLinker<K?> {
        return withContext(Dispatchers.IO) {
            CoroutineLinker(block(), scope)
        }
    }

    suspend infix fun ui(block: suspend (value: T & Any) -> Unit): CoroutineLinker<T> {
        return withContext(Dispatchers.Main) {
            // 仅当value非空时执行ui块,且传递非空参数
            value?.let { nonNullValue ->
                block(nonNullValue)
            }
            this@CoroutineLinker
        }
    }

    suspend infix fun ifNull(block: suspend () -> Unit) {
        withContext(Dispatchers.Main) {
            // 仅当value为空时执行
            if (value == null) {
                block()
            }
        }
    }
}

// 可选便捷入口,无需手动创建CoroutineLinker实例
fun <T> CoroutineScope.launchLink(ioBlock: suspend () -> T?): Job {
    return launch {
        CoroutineLinker(ioBlock(), this@launchLink)
    }
}

注:T & Any是Kotlin 1.7+支持的泛型非空表示方法,用来获取泛型T的非空版本,如果项目Kotlin版本低于1.7,可以把参数类型改为T,实际运行时传递的也是非空值,仅编译期不会自动提示非空,不需要手动判空即可使用。


调用示例

完全符合预期的写法,ui块无需手动判空,ifNull块仅在IO返回空时执行:

// 建议使用和生命周期绑定的Scope,比如Android的lifecycleScope、viewModelScope
lifecycleScope.launch {
    io {
        // IO操作,返回可空对象
        getUserInfoFromServer(userId)
    } ui { nonNullUser ->
        // 此处拿到的nonNullUser自动为非空类型,直接使用即可
        renderUserInfo(nonNullUser)
    } ifNull {
        // IO返回空时才会执行此处
        showEmptyState()
    }
}

优化建议

  • 避免使用GlobalScope:传入和页面/组件生命周期绑定的协程Scope,页面销毁时自动取消未完成的协程任务,避免内存泄漏
  • 新增异常捕获:可以给io、ui函数增加异常回调参数,捕获执行过程中的异常避免崩溃,示例如下:
suspend infix fun <K> io(
    block: suspend () -> K?,
    onError: suspend (Throwable) -> Unit = {}
): CoroutineLinker<K?> {
    return withContext(Dispatchers.IO) {
        val result = runCatching { block() }
            .onFailure { onError(it) }
            .getOrNull()
        CoroutineLinker(result, scope)
    }
}
  • 扩展调度器支持:可以按需新增default、blockingIo等其他调度器的链式调用函数,覆盖计算密集型任务、阻塞IO任务等不同场景
  • 支持返回值透传:如果需要多段IO、UI交替执行的链式调用,可以保留返回CoroutineLinker的逻辑,支持更长的链式调度流程

内容的提问来源于stack exchange,提问作者JonZarate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 12:42:03