Como chamar a solicitação assíncrona de forma síncrona com um valor de tempo limite?
Eu tenho que chamar de forma síncrona uma solicitação de API assíncrona. Como essa solicitação de api leva muito tempo para responder, também quero definir um tempo limite para falhar a solicitação de api e continuar com nulo. Este é meu código para chamar esta api:
private suspend fun call(
accessStage: AccessStage,
): Response? = withContext<Response?>(Dispatchers.IO) {
return@withContext withTimeoutOrNull(1000) {
suspendCoroutine<Response?> { continuation ->
val request = External3rdPartyApi.newRequest(
accessStage
) { response, throwable ->
continuation.resume(response)
}
request.parameters = hashMapOf<String, String>().apply {
put["token"] = External3rdPartyApi.TOKEN
put["salt"] = External3rdPartyApi.calculateSalt(accessStage)
}
request.executeAsync()
}
}
}
Eu não posso mudar como External3rdPartyApi
funciona.
Acho que o código acima parece mal. Além disso, li em outra resposta :
withTimeout { ... }
foi projetado para cancelar a operação em andamento no tempo limite, o que só é possível se a operação em questão for cancelável .
Então, devo usar em suspendCancellableCoroutine
vez de suspendCoroutine
?
Como posso escrever de uma maneira melhor?
Respostas
Não há problema em usar suspendCoroutine
se você não puder (ou não quiser) o cancelamento da co-rotina. Mas como você tem um tempo limite, você deve considerar o uso de suspendCancellableCoroutine e manipular o evento de cancelamento para interromper o trabalho (na função de terceiros - se possível).
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
Quando sua função de terceiros lança uma exceção, você pode tentar capturá-la e terminar sua continuação usando retomar com exception
ou retornar um null
valor (depende do seu caso de uso):
suspendCancellableCoroutine<T?> { continuation ->
try {
continuation.resume(thirdParty.call())
} catch (e: Exception) {
// resume with exception
continuation.resumeWithException(e)
// or just return null and swallow the exception
continuation.resume(null)
}
}
Vamos colocar tudo junto
suspend fun call(): Response? = withContext(Dispatchers.IO) {
return@withContext withTimeoutOrNull(1000L) {
return@withTimeoutOrNull suspendCancellableCoroutine { continuation ->
try {
continuation.resume(External3rdPartyApi.newRequest(accessStage))
} catch (e: Exception) {
// resume with exception
continuation.resumeWithException(e)
// or just return null and swallow the exception
continuation.resume(null)
}
// in case the coroutine gets cancelled - because of timeout or something else
continuation.invokeOnCancellation {
// stop all the work
}
}
}
}