Edit 2: I think I misunderstood the documentation. I read:
runBlocking
This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
main
functions and in tests.
To mean that I shouldn't use runBlocking()
at all other than for main
or tests. But I now realise I read it wrong, specifically this part:
It is designed to bridge regular blocking code to libraries that are written in suspending style
So it seems that runBlocking should be used in this scenario.
However I think I should fully grok the topic of contexts to see which contexts is best suited to pass to runBlocking in this case:
return runBlocking(???????) {
job1.await() + job2.await()
}
Edit: Clearly I phrased the question badly as all attempts to answer it miss the actual question and the constraint I am posing. So let's try a different approach...
This works:
fun doSomething(): Int {
val job1 = GlobalScope.async { calculateSomething() }
val job2 = GlobalScope.async { calculateSomething() }
return runBlocking {
job1.await() + job2.await()
}
}
suspend fun calculateSomething(): Int {
delay(1000L)
return 13
}
suspend fun calculateSomethingElse(): Int {
delay(2000L)
return 19
}
My question is: is there anyway I can achieve the same result:
runBlocking()
at all.doSomething()
into a suspend
function.?
In other words: is there anything I can put instead of ??????
to make the following work?
fun doSomething(): Int {
val job1 = GlobalScope.async { calculateSomething() }
val job2 = GlobalScope.async { calculateSomething() }
return ????????? {
job1.?????() + job2.?????()
}
}
suspend fun calculateSomething(): Int {
delay(1000L)
return 13
}
suspend fun calculateSomethingElse(): Int {
delay(2000L)
return 19
}
I have a small utility method that runs any given external command and returns its output (i.e small wrapper around Java Process API):
class RunningCommand(private val proc: Process) {
fun waitFor(): String {
proc.outputStream.close()
var output = ""
val outputRdr = thread { output = proc.inputStream.bufferedReader().use { it.readText() } }
var error = ""
val errorRdr = thread { error = proc.errorStream.bufferedReader().use { it.readText() } }
proc.waitFor()
outputRdr.join()
errorRdr.join()
if (proc.exitValue() != 0) {
throw RuntimeException("Command returned non-zero status: $output$error")
}
return output
}
}
This code works correctly. However it creates two additional threads for each command execution. I wanted to avoid that by switching to coroutines. I was able to do this, but I had to use runBlocking
:
class RunningCommand(private val proc: Process) {
fun waitFor(): String {
proc.outputStream.close()
var output = ""
val outputRdr = GlobalScope.async { output = proc.inputStream.bufferedReader().use { it.readText() } }
var error = ""
val errorRdr = GlobalScope.async { error = proc.errorStream.bufferedReader().use { it.readText() } }
proc.waitFor()
runBlocking {
outputRdr.await()
errorRdr.await()
}
if (proc.exitValue() != 0) {
throw RuntimeException("Command returned non-zero status: $output${error}")
}
return output
}
}
This code also works, but I read that runBlocking
should only be used on main()
methods and tests, i.e not meant to be used in this way. Peeking at its implementation it looks horrible and indeed looks like something one wouldn't want to call repeatedly from some utility method.
So my question is: how else am I supposed to bridge the gap between blocking code and coroutines? Or put differently, what is the correct way to wait for a suspend
function from non-suspend
code?
Or is it simply that my design is wrong, and to use Coroutines anywhere down the line I need to make the main()
method runBlocking
and essentially always be inside some coroutine scope?
For any future travellers who make the same mistake as me - runBlocking
is OK to use not just in main
/ tests - but also:
It is designed to bridge regular blocking code to libraries that are written in suspending style
Somehow I got the impression that it is evil to use for just some library function, but it is not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With