如何使用超时值同步调用异步请求?
How to call asynchronous request synchronously with a timeout value?
我必须同步调用异步 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 { ... }
is designed to cancel the ongoing operation on timeout, which is only possible if the operation in question is cancellable.
那么,我应该使用 suspendCancellableCoroutine
而不是 suspendCoroutine
吗?
我怎样才能写得更好?
如果您不能(或不想)处理协程的取消,则使用 suspendCoroutine
没问题。但是因为你有超时,你应该考虑使用 suspendCancellableCoroutine 并处理取消事件以停止工作(在第三方函数中 - 如果可以的话)。
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
当您的第三方函数抛出异常时,您可以尝试捕获它并使用 exception
或 return 一个 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
}
}
}
}
我必须同步调用异步 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 { ... }
is designed to cancel the ongoing operation on timeout, which is only possible if the operation in question is cancellable.
那么,我应该使用 suspendCancellableCoroutine
而不是 suspendCoroutine
吗?
我怎样才能写得更好?
如果您不能(或不想)处理协程的取消,则使用 suspendCoroutine
没问题。但是因为你有超时,你应该考虑使用 suspendCancellableCoroutine 并处理取消事件以停止工作(在第三方函数中 - 如果可以的话)。
suspendCancellableCoroutine<T> { continuation ->
continuation.invokeOnCancellation { throwable ->
// now you could stop your (third party) work
}
}
当您的第三方函数抛出异常时,您可以尝试捕获它并使用 exception
或 return 一个 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
}
}
}
}