Следует избегать первого подхода, поскольку он блокирует поток в пуле потоков.Используя второй подход, вы можете распространять отмену в обоих направлениях.Если вы отмените Deferred
, он отменит вызов, а если вызов не удастся, он отменит Deferred
с исключением, которое он получил.
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
наверное красная сельдь.Если вы отменяете его, основная причина в том, что вы спасаетесь от всей задачи, которую делаете.Вместо этого вы должны отменить всю сопрограмму, в которой она выполняется. Если вы правильно реализуете структурированный параллелизм , все будет происходить автоматически, если ваша активность будет разрушена.
Поэтому я рекомендую использовать этот код:
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
, потому что вы запускаете его одновременно в фоновом режиме, это легко сделать с помощью приведенного выше:
val userDeferred = this.async { getUser() }
Где я предполагаю this
ваша деятельность, которая также является CoroutineScope
.