Làm thế nào để gọi yêu cầu không đồng bộ một cách đồng bộ với một giá trị thời gian chờ?

Jan 07 2021

Tôi phải gọi đồng bộ một yêu cầu api không đồng bộ. Vì yêu cầu api này mất nhiều thời gian để trả lời, tôi cũng muốn đặt thời gian chờ để yêu cầu api không thành công và tiếp tục với null. Đây là mã của tôi để gọi api này:

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()
        }
    }
}

Tôi không thể thay đổi cách thức External3rdPartyApihoạt động.

Tôi nghĩ rằng đoạn mã trên trông có vẻ ác. Ngoài ra, tôi đã đọc trong một câu trả lời khác :

withTimeout { ... }được thiết kế để hủy hoạt động đang diễn ra khi hết thời gian chờ, điều này chỉ có thể thực hiện được nếu hoạt động được đề cập có thể hủy được .

Vì vậy, tôi có nên sử dụng suspendCancellableCoroutinethay vì suspendCoroutine?

Làm thế nào tôi có thể viết nó theo cách tốt hơn?

Trả lời

1 ChristianB Jan 07 2021 at 19:42

Việc sử dụng suspendCoroutinecũng được nếu bạn không thể (hoặc không muốn) xử lý việc hủy Đăng ký. Nhưng vì bạn có thời gian chờ, bạn nên cân nhắc sử dụng pauseCancellableCoroutine và xử lý sự kiện hủy để dừng công việc (trong chức năng của bên thứ ba - nếu bạn có thể).

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

Khi hàm bên thứ ba của bạn ném ra một ngoại lệ, bạn có thể thử bắt nó và hoàn thành việc tiếp tục bằng cách tiếp tục với exceptionhoặc trả về một nullgiá trị (tùy thuộc vào trường hợp sử dụng của bạn):

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)
  }   
}

Hãy tập hợp tất cả lại với nhau

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
      }
    }
  }
}