Reputation: 737
I'm just starting learning coroutines and managed to write a simple blocking one, that's aimed on a retry handling of a remote rest service. It returns me an eception if he can't currently handle foo update. But we have an agreement that i'll try to update foo next 10 seconds with an interval of 1 second:
private fun updateFoo(fooId: String): Foo {
val updatedFoo = runBlocking {
return@runBlocking withTimeoutOrNull(10L) {
var result: Foo?
do {
result = try {
fooClient.update(fooId)
} catch (ex: Exception) {
null
}
if (result != null) {
return@withTimeoutOrNull result
}
delay(1L)
} while (result == null)
return@withTimeoutOrNull result
}
}
return updatedFoo ?: throw IllegalStateException(
"Could not update Foo ($fooId) with retries.")
}
The point is that i run coroutine blocked. And ten seconds is a big enough interval, so i want to refactor this method to run coroutine nonBlcking.
I've tried to use launch { ... } instead of runBlocking, but i get Job as a result, but that's not what i want, i guess. Could anyone help me to write it correctly???
Upvotes: 0
Views: 1165
Reputation: 19565
Assuming fooClient.update
is already a suspending function, then your problem really seems to be about how to bridge the non-coroutines world that updateFoo
lives in (as it is a non-suspending function) with the coroutines world of fooClient.update
.
If that interpretation of the question is correct, then you'll need to use a traditional mechanism to obtain an async result from updateFoo
, such as a deferred result or a callback.
If your updateFoo
code ran in an async
block you could return the Deferred<Foo?>
value e.g.:
fun updateFoo(fooId: String): Deferred<Foo?> {
return GlobalScope.async {
return@async withTimeoutOrNull(10L) {
var result: Foo?
do {
result = try {
update(fooId)
} catch (ex: Exception) {
null
}
if (result != null) {
return@withTimeoutOrNull result
}
delay(1L)
} while (result == null)
return@withTimeoutOrNull result
}
}
}
and then you could convert that to a CompletableFuture
in your non-coroutines code e.g.
asCompletableFuture()
NOTE: I've used GlobalScope
here for simplicity buts its generally a good idea to use an explicit scope, depending on your application's requirements.
Your commented that fooClient.update
is actually a non-suspending blocking function. Given that, you should run it in an appropriate pool of threads that is configured specifically for blocking calls like this, such as Dispatchers.IO
:
withContext(Dispatchers.IO) {
update(fooId)
}
When run like this, the coroutine will not block -- the withContext
call suspends.
Upvotes: 1