จะเรียกคำขอแบบอะซิงโครนัสพร้อมกับค่าการหมดเวลาได้อย่างไร
ฉันต้องเรียกคำขอ API แบบอะซิงโครนัสแบบซิงโครนัส เนื่องจากคำขอ api นี้ใช้เวลาตอบนานฉันจึงต้องการตั้งค่าการหมดเวลาเพื่อล้มเหลวในการร้องขอ api และดำเนินการต่อด้วย null นี่คือรหัสของฉันที่จะเรียก 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()
}
}
}
ฉันไม่สามารถเปลี่ยนวิธีการExternal3rdPartyApi
ทำงานได้
ฉันคิดว่าโค้ดข้างบนดูชั่วร้าย นอกจากนี้ฉันอ่านในคำตอบอื่น :
withTimeout { ... }
ได้รับการออกแบบมาเพื่อยกเลิกการดำเนินการที่กำลังดำเนินอยู่เมื่อหมดเวลาซึ่งจะทำได้ก็ต่อเมื่อการดำเนินการที่เป็นปัญหานั้นสามารถยกเลิกได้
ดังนั้นฉันควรใช้suspendCancellableCoroutine
แทนsuspendCoroutine
?
ฉันจะเขียนให้ดีขึ้นได้อย่างไร
คำตอบ
การใช้งานsuspendCoroutine
ทำได้ดีหากคุณไม่สามารถ (หรือไม่ต้องการ) จัดการกับการยกเลิก Coroutine แต่เนื่องจากคุณหมดเวลาคุณควรพิจารณาใช้suspendCancellableCoroutineและจัดการเหตุการณ์การยกเลิกเพื่อหยุดการทำงาน (ในฟังก์ชันของบุคคลที่สาม - ถ้าคุณทำได้)
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
เมื่อฟังก์ชั่นบุคคลที่สามของคุณเกิดข้อยกเว้นคุณสามารถลองจับมันและดำเนินการต่อให้เสร็จโดยใช้การเริ่มต้นใหม่ด้วยค่าexception
หรือส่งคืนnull
ค่า (ขึ้นอยู่กับกรณีการใช้งานของคุณ):
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)
}
}
มารวมกัน
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
}
}
}
}