시간 제한 값을 사용하여 비동기 요청을 동기식으로 호출하는 방법은 무엇입니까?

Jan 07 2021

비동기 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합니까?

더 나은 방법으로 작성하려면 어떻게해야합니까?

답변

1 ChristianB Jan 07 2021 at 19:42

suspendCoroutine루틴 취소를 처리 할 수 ​​없거나 원하지 않는 경우 사용하는 것이 좋습니다. 그러나 시간 초과가 있기 때문에 suspendCancellableCoroutine 사용을 고려 하고 취소 이벤트를 처리하여 작업을 중지해야합니다 (가능한 경우 타사 함수에서).

suspendCancellableCoroutine<T> { continuation ->
  continuation.invokeOnCancellation { throwable ->
    // now you could stop your (third party) work  
  }        
}

제 3 자 함수가 예외를 던질 때, 당신은 그것을 try-catch 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
      }
    }
  }
}