Kotlin. Coroutines: How to run repeatable job?

266 views Asked by At

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.

3

There are 3 answers

2
Sergio On

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
    }
}
0
Tenfour04 On

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" }
    }
}
0
sergpetrov On

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