Wie rufe ich eine asynchrone Anfrage synchron mit einem Timeout-Wert auf?
Ich muss synchron eine asynchrone API-Anfrage aufrufen. Da die Beantwortung dieser API-Anfrage lange dauert, möchte ich auch eine Zeitüberschreitung festlegen, um die API-Anfrage fehlzuschlagen und mit null fortzufahren. Hier ist mein Code, um diese API aufzurufen:
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()
}
}
}
Ich kann nicht ändern, wie es External3rdPartyApi
funktioniert.
Ich denke, der obige Code sieht böse aus. Außerdem habe ich in einer anderen Antwort gelesen :
withTimeout { ... }
ist so konzipiert, dass der laufende Vorgang bei Zeitüberschreitung abgebrochen wird. Dies ist nur möglich, wenn der betreffende Vorgang abgebrochen werden kann .
Also sollte ich suspendCancellableCoroutine
statt verwenden suspendCoroutine
?
Wie kann ich es besser schreiben?
Antworten
Die Verwendung suspendCoroutine
ist in Ordnung, wenn Sie die Stornierung der Coroutine nicht handhaben können (oder wollen). Da Sie jedoch eine Zeitüberschreitung haben, sollten Sie die Verwendung von suspendCancellableCoroutine in Betracht ziehen und das Abbruchereignis behandeln, um die Arbeit zu stoppen (in der Drittanbieterfunktion - wenn Sie können).
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
Wenn Ihre Drittanbieterfunktion eine Ausnahme auslöst, können Sie versuchen, sie abzufangen und Ihre Fortsetzung zu beenden, indem Sie entweder mit dem Wert fortfahren exception
oder einen null
Wert zurückgeben (abhängig von Ihrem Anwendungsfall):
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)
}
}
Lasst uns alles zusammenfügen
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
}
}
}
}