¿Cómo llamar a una solicitud asincrónica sincrónicamente con un valor de tiempo de espera?
Tengo que llamar sincrónicamente a una solicitud de API asincrónica. Dado que esta solicitud de API tarda mucho en responder, también quiero establecer un tiempo de espera para fallar la solicitud de API y continuar con null. Aquí está mi código para llamar a 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()
}
}
}
No puedo cambiar cómo External3rdPartyApi
funciona.
Creo que el código anterior parece malvado. Además, leí en otra respuesta :
withTimeout { ... }
está diseñado para cancelar la operación en curso en el tiempo de espera, que solo es posible si la operación en cuestión es cancelable .
Entonces, ¿debería usar en suspendCancellableCoroutine
lugar de suspendCoroutine
?
¿Cómo puedo escribirlo mejor?
Respuestas
El uso suspendCoroutine
está bien si no puede (o no quiere) manejar la cancelación de Coroutine. Pero debido a que tiene un tiempo de espera, debe considerar usar suspendCancellableCoroutine y manejar el evento de cancelación para detener el trabajo (en la función de terceros, si puede).
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
Cuando la función de un tercero arroja una excepción, puede intentar capturarla y finalizar su continuación utilizando reanudar con exception
o devolver un null
valor (depende de su 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 a juntar todo
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
}
}
}
}