Bagaimana cara memanggil permintaan asinkron secara sinkron dengan nilai batas waktu?
Saya harus secara sinkron memanggil permintaan api asynchronous. Karena permintaan api ini membutuhkan waktu lama untuk dijawab, saya juga ingin mengatur waktu tunggu untuk gagal dalam permintaan api dan melanjutkan dengan null. Ini kode saya untuk memanggil api ini:
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()
}
}
}
Saya tidak bisa mengubah cara External3rdPartyApi
kerjanya.
Saya pikir kode di atas terlihat jahat. Juga, saya membaca di jawaban lain :
withTimeout { ... }
dirancang untuk membatalkan operasi yang sedang berlangsung saat batas waktu, yang hanya mungkin jika operasi yang dimaksud dapat dibatalkan .
Jadi, yang harus saya gunakan suspendCancellableCoroutine
bukan suspendCoroutine
?
Bagaimana saya bisa menulisnya dengan cara yang lebih baik?
Jawaban
Penggunaannya suspendCoroutine
baik-baik saja jika Anda tidak dapat (atau tidak ingin) menangani pembatalan Coroutine. Tetapi karena Anda memiliki waktu tunggu, Anda harus mempertimbangkan untuk menggunakan suspendCancellableCoroutine dan menangani peristiwa pembatalan untuk menghentikan pekerjaan (dalam fungsi pihak ketiga - jika Anda bisa).
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
Saat fungsi pihak ketiga Anda melontarkan pengecualian, Anda dapat mencoba menangkapnya dan menyelesaikan kelanjutan Anda menggunakan melanjutkan dengan exception
atau mengembalikan null
nilai (bergantung pada kasus penggunaan Anda):
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)
}
}
Ayo kumpulkan semuanya
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
}
}
}
}