You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让已完成的CompletableDeferred恢复未完成状态?及替代方案咨询

问题解答

1. CompletableDeferred无法恢复为未完成状态

CompletableDeferred的设计本质就是一次性完成的,一旦调用complete()、completeExceptionally()或者被取消,它的状态就永久固定了,没有任何官方API能把它重置回未完成状态。所以你没法通过单个CompletableDeferred实现“每次重试都等待用户点击”的需求。

2. 更合适的替代方案

方案一:使用Channel(推荐)

Channel支持多次发送信号,每次重试时从Channel中获取新的触发信号,完美匹配“每次异常都等用户点击再重试”的场景。修改后的代码如下:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // CONFLATED模式避免重复点击导致信号堆积,只保留最新的一次点击信号
    val retryChannel = Channel<Unit>(Channel.CONFLATED)

    lifecycleScope.launch {
        flow {
            for (x in 1..10) {
                if (x == 2)
                    throw Throwable()
                emit(x)
            }
        }.retryWhen { _, _ ->
            Log.w("L384", "retryWhen")
            retryChannel.receive() // 阻塞等待用户点击的信号
            true
        }.collect {
            Log.w("L384", "$it")
        }
    }

    val retryBtn: Button = findViewById(R.id.btn)
    retryBtn.setOnClickListener {
        lifecycleScope.launch {
            retryChannel.send(Unit) // 点击时发送重试触发信号
        }
    }
}

方案二:使用MutableStateFlow

通过StateFlow维护一个可重置的触发状态,每次点击切换状态,重试后再重置状态:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val retryTrigger = MutableStateFlow(false)

    lifecycleScope.launch {
        flow {
            for (x in 1..10) {
                if (x == 2)
                    throw Throwable()
                emit(x)
            }
        }.retryWhen { _, _ ->
            Log.w("L384", "retryWhen")
            retryTrigger.first { it } // 等待状态变为true(用户点击)
            retryTrigger.value = false // 重置状态,确保下次重试需要再次点击
            true
        }.collect {
            Log.w("L384", "$it")
        }
    }

    val retryBtn: Button = findViewById(R.id.btn)
    retryBtn.setOnClickListener {
        retryTrigger.value = true
    }
}

方案三:每次重试创建新的CompletableDeferred

如果一定要用CompletableDeferred,可以在retryWhen中每次创建新的实例,替代复用同一个对象:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var currentRetrySignal: CompletableDeferred<Unit>? = null

    lifecycleScope.launch {
        flow {
            for (x in 1..10) {
                if (x == 2)
                    throw Throwable()
                emit(x)
            }
        }.retryWhen { _, _ ->
            Log.w("L384", "retryWhen")
            currentRetrySignal = CompletableDeferred() // 每次重试创建新的信号
            currentRetrySignal?.await()
            true
        }.collect {
            Log.w("L384", "$it")
        }
    }

    val retryBtn: Button = findViewById(R.id.btn)
    retryBtn.setOnClickListener {
        currentRetrySignal?.complete(Unit) // 触发当前的重试信号
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:35:22