Comment appeler une demande asynchrone de manière synchrone avec une valeur de délai d'expiration?
Je dois appeler de manière synchrone une demande d'API asynchrone. Étant donné que cette demande d'API prend beaucoup de temps à répondre, je souhaite également définir un délai d'expiration pour échouer la demande d'API et continuer avec null. Voici mon code pour appeler cette 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()
}
}
}
Je ne peux pas changer comment External3rdPartyApi
fonctionne.
Je pense que le code ci-dessus a l'air maléfique. Aussi, j'ai lu dans une autre réponse :
withTimeout { ... }
est conçu pour annuler l'opération en cours sur timeout, ce qui n'est possible que si l'opération en question est annulable .
Alors, devrais-je utiliser à la suspendCancellableCoroutine
place de suspendCoroutine
?
Comment puis-je mieux l'écrire?
Réponses
L'utilisation suspendCoroutine
est très bien si vous ne pouvez pas (ou ne voulez pas) gérer l'annulation de la Coroutine. Mais comme vous avez un délai d'expiration, vous devriez envisager d'utiliser suspendCancellableCoroutine et gérer l'événement d'annulation pour arrêter le travail (dans la fonction tierce - si vous le pouvez).
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
Lorsque votre fonction tierce lève une exception, vous pouvez essayer de l'attraper et terminer votre continuation en utilisant soit la reprise avec le exception
soit le retour d'une null
valeur (dépend de votre cas d'utilisation):
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)
}
}
Permet de tout mettre ensemble
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
}
}
}
}