超时后如何取消协程作业

时间:2019-09-04 20:54:37

标签: kotlin-coroutines withcontext withtimeoutornull

具有暂停功能fetchData()。它的作用是在withContext中启动一些作业,以便仅在作业完成后才返回(即:suspend fun getData(): Boolean)。

还希望它是否超时,然后从该函数返回false。

问题是当它withTimeoutOrNull(500) { jobs.joinAll() }超时时,它停留在函数中而没有退出。

日志显示超时,并且在退出函数之前也清楚地指向代码的最后一行:

     E/+++: +++ in fetchData() after null = withTimeoutOrNull(500), jobs.sizs: 3
     E/+++: +++ --- exit fetchData(), allFresh: false

但是fetchData()的呼叫者被卡住,无法从fetchData()返回。

这是呼叫者:

suspend fun caller() {
    var allGood = fetchData()

    // never return to here

    Log.e("+++", "+++ caller(), after allGood: $allGood = fetchData()")
    ...
}

下面是代码,如果超时,如何取消作业?

suspend fun fetchData(): Boolean = withContext(Dispatchers.IO) {

    var allFresh = requestHandlertMap.size > 0
    if (!allFresh) {
        allFresh
    } else {
        val handlers = requestHandlertMap.values.toList()
        val jobs: List<Deferred<Boolean>> = handlers.map {handler->
            async(start = CoroutineStart.LAZY) {
                if (isActive) handler.getData() else true
                        .also {
                            Log.e("+++", "+++ in fetchData():async{} after handler.getData()")
                        }
            }
        }
        val result = withTimeoutOrNull(500) { jobs.joinAll() }

        Log.e("+++", "+++ in fetchData() after $result = withTimeoutOrNull(500), jobs.size: ${jobs.size} ")

        if (result != null) {
            allFresh = jobs.all { deferred ->
                deferred.await()
            }
            Log.e("+++", "+++ +++ +++ in fetchData() call  onDataReady(), allFresh: $allFresh = deferred.await() ")
            onDataReady()
        } else {

            // how to cancel the jobs ???

            //jobs.all { deferred ->
                //deferred.cancelChildren()
            //}

            allFresh = false
        }
        allFresh
                .also {
                    Log.e("+++", "+++ --- exit fetchData(), allFresh: $allFresh  ")
                }
    }
}

1 个答案:

答案 0 :(得分:0)

在阅读/尝试之后,似乎在实现方面遇到了一些问题。

  1. 以某种方式CoroutineStart.LAZY导致一种奇怪的行为,即async(start = CoroutineStart.LAZY) 按顺序开始(期望它们应该同时开始),以便在超时时卡在函数中(猜测是因为它包装在withContext(Dispatchers.IO)中,并且并非所有子协程都完成了-如果有人不开始)。

删除start = CoroutineStart.LAZY使其从fun fetchData()返回

val jobs: List<Deferred<Boolean>> = handlers.map {handler->
            async(start = CoroutineStart.LAZY) {
                if (isActive) handler.getData() else true
                        .also {
                            Log.e("+++", "+++ in fetchData():async{} after handler.getData()")
                        }
            }
        }
  1. 尚未实现suspend fun getData(): Boolean cooperate to be cancellable,这可能导致它仍然停留在函数中,直到所有子项都完成为止,尽管已经发生了超时。

  2. 似乎仍然需要调用deferred.cancelChildren(),否则withTimeoutNotNull()不会取消它们,不知道为什么,它不应该自动取消作业吗?

    < / li>

所做的更改

private suspend fun fetchData(): Boolean {
        var allFresh: Boolean? = requestHandlertMap.size > 0
        if (allFresh == true) {

            val handlers = requestHandlertMap.values.toList()
            val jobs: List<Deferred<Boolean>> = handlers.map {
                serviceScope.async(start = CoroutineStart.DEFAULT) { handler -> if (isActive) handler.getData() else false }
            }
            allFresh = withTimeoutOrNull(3000) {
                try {
                    jobs.awaitAll().all { it }
                } catch (ex: Throwable) {
                    false
                }
            }

            if (allFresh != null) {
                onDataReady()
            } else {
                jobs.map { deferred -> deferred.cancelChildren() }
            }
        }
        return allFresh == true // allFresh = {null, true, false}
    }

ref:herehere