Home > Software engineering >  Kotlin. Coroutines: How to run repeatable job?
Kotlin. Coroutines: How to run repeatable job?

Time:10-18

I need to make api call before until the server sends a success status. The request must be executed with a delay of two seconds, and all this must be done until 10 seconds have elapsed.

Here are my attempts:

suspend fun loadData(): Data {
    try {
        withTimeout(10000) {
            val data = repository.getData()
            if (data.status == "SUCCESS") {
                // complete
            } else {
                delay(1000)
                // and after delay I need again call repository.getData(), until the status is SUCCESS
            }
        }
    } catch (e: Exception) {
        // complete
    }

}

But I do not understand how to loop the execution of the request and it seems to me that something is wrong with my code. Perhaps there is a more concise way, please help me.

CodePudding user response:

I believe you can use a simple while loop for that:

suspend fun loadData(): Data? {
    return try {
        withTimeout(10000) {
            var data = repository.getData()
            while (data.status != "SUCCESS") {
                delay(1000)
                data = repository.getData()
            }
            return@withTimeout data
        }
    } catch (e: Exception) {
        null
    }
}

CodePudding user response:

Alternate solution using a flow. I think it makes it cleaner to do this while being able to handle exceptions thrown from getData() while continuing to retry. In your original code, it looks like you are going to give up the first time an exception is thrown by getData() instead of continuing to retry every 1000ms.

The return type has to be nullable to handle the case where it isn't retrieved in time.

suspend fun loadData(): Data? {
    return withTimeoutOrNull(10000) {
        flow { 
            while(true) { 
                emit(repository.getData())
                delay(1000)
            }
        }
            .retry { delay(1000) }
            .first { it.status == "SUCCESS" }
    }
}

CodePudding user response:

This is my approach with repeatWhen of flow without while loop:

suspend fun loadData(): Data? {
        return withTimeout(10000) {
            flow {
                val result = repository.getData()
                if (result.status == "SUCCESS") {
                    emit(result)
                } else throw ServerException()
            }.retryWhen { cause, _ ->
                if (cause is ServerException) {
                    delay(1000)
                    true
                } else false
            }.singleOrNull()
        }
}
  • Related