首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CoroutineScope - CompletableDeferred取消

CoroutineScope - CompletableDeferred取消
EN

Stack Overflow用户
提问于 2018-10-12 07:37:43
回答 2查看 4.6K关注 0票数 3

关于这个话题,我有两个问题。我将在android中使用这些用例类,并尝试实现类似于这个https://www.youtube.com/watch?v=Sy6ZdgqrQp0的体系结构,但我需要一些答案。

1)我有一个带有异步构建器的延迟,当我取消作业时,其他链也被取消了。此代码打印“呼叫取消”。但我不确定我做得对不对。

代码语言:javascript
复制
fun main(args: Array<String>) = runBlocking<Unit> {
    val job = GlobalScope.launch {
        println(getUser())
    }
    job.cancelAndJoin()
}

suspend fun getUser() = getUserDeferred().await()


suspend fun getUserDeferred() = coroutineScope {

    val request = Request.Builder()
            .url("https://jsonplaceholder.typicode.com/users")
            .build()

    val call = OkHttpClient().newCall(request)

    val deferred = async(Dispatchers.IO) {
        val body = call.execute()
        body.body()?.string() ?: ""
    }

    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            println("Call cancelled")
            call.cancel()
        }
    }
    deferred
}

2)我想不出取消这个计划的办法。我想在retrofit2调用适配器中使用这一点,是否有更好的方法来处理这种情况。

代码语言:javascript
复制
fun main(args: Array<String>) = runBlocking<Unit> {
    val job = GlobalScope.launch {
        println(getUser1())
    }
    job.cancelAndJoin()
}

suspend fun getUser1() = getUser1Deferred().await()


fun getUser1Deferred(): Deferred<String> {
    val request = Request.Builder()
            .url("https://jsonplaceholder.typicode.com/users")
            .build()

    val call = OkHttpClient().newCall(request)

    val deferred = CompletableDeferred<String>()

    call.enqueue(object : Callback {

        override fun onFailure(call: Call, e: IOException) {
            deferred.complete("Error")
        }

        override fun onResponse(call: Call, response: Response) {
            deferred.complete(response.body()?.string() ?: "Error")
        }

    })

    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            println("Call cancelled")
            call.cancel()
        }
    }
    return deferred
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-10-12 08:36:56

您应该避免使用第一种方法,因为它会阻塞线程池中的线程。使用第二种方法,您可以通过两种方式传播取消。如果您取消了Deferred,它将取消调用,如果调用失败,它将取消Deferred,除非它得到异常。

代码语言:javascript
复制
fun getUserAsync(): Deferred<String> {
    val call = OkHttpClient().newCall(Request.Builder()
            .url("https://jsonplaceholder.typicode.com/users")
            .build())
    val deferred = CompletableDeferred<String>().apply {
        invokeOnCompletion {
            if (isCancelled) {
                call.cancel()
            }
        }
    }
    call.enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            deferred.complete(response.body()?.string() ?: "Error")
        }
        override fun onFailure(call: Call, e: IOException) {
            deferred.cancel(e)
        }

    })
    return deferred
}

然而,走Deferred路线可能是一条红鱼。如果你要取消它,最根本的原因是你要放弃你正在做的全部任务。你应该取消它运行的整个协同线。如果您正确地实现了结构化并发,那么当您的活动被破坏时,一切都会自动发生。

所以我的建议是使用以下代码:

代码语言:javascript
复制
suspend fun getUser() = suspendCancellableCoroutine<String> { cont ->
    val call = OkHttpClient().newCall(Request.Builder()
            .url("https://jsonplaceholder.typicode.com/users")
            .build())
    cont.invokeOnCancellation {
        call.cancel()
    }
    call.enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            cont.resume(response.body()?.string() ?: "Error")
        }
        override fun onFailure(call: Call, e: IOException) {
            cont.resumeWithException(e)
        }

    })
}

如果您绝对需要Deferred,因为您是在后台并发运行它,那么使用上面的内容是很容易的:

代码语言:javascript
复制
val userDeferred = this.async { getUser() }

假设this是您的活动,它也是一个CoroutineScope

票数 4
EN

Stack Overflow用户

发布于 2019-03-27 17:55:14

第二种情况不是取消的原因是因为您使用的是CompletableDeferred。它不是作为协同线启动的,也不是你父母协同机制的孩子。因此,如果您取消父级,它将不会取消延迟。

在第一种情况下,它可以工作,因为async启动了一个新的子协同线,它是链接到父节点的。当你取消任何一个,他们都会被取消。

为了将Deferred链接到父作业,需要引用它并使用invokeOnCompletion

代码语言:javascript
复制
var deferred : Deferred<Void>? = null
launch {        
   deferred = retroService.someDeferredCall()
   deferred.await()
}.invokeOnCompletion {
   //job was cancelled.  Probably activity closing.
   if(it is CancellationException) {
      deferred?.let { it.cancel() }
   }
}

不是很漂亮,但应该能把工作做完。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52774588

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档