Hey all, I am pretty new to using coroutines in g...
# coroutines
h
Hey all, I am pretty new to using coroutines in general and I'm still struggling with Kotlin coroutines. I have dabbled with some async in Javascript and Python but nothing in Kotlin. I wrote the following code after some reading and I'm wondering why this may be a bad idea:
Copy code
fun asyncOp(nums: List<Int>, mult: Int, id: String) = GlobalScope.async {
    println("1 - $id: $mult times list computing asynchronously")
    Thread.sleep(2000L * mult)
    println("2 - $id: $mult times list computing asynchronously")
    Thread.sleep(1000L * mult)
    nums.map { it * mult }
}

suspend fun main() {

    val nums = listOf(1,2,3,4,5,6)
    val double = asyncOp(nums, 2,"first")
    val triple = asyncOp(nums, 3,"second")

    println("Double requested: $double")
    println("Triple requested: $triple")

    println("Waiting...")

    println(awaitAll(triple, double))
}
This works as intended, but Intellij suggested this instead:
Copy code
suspend fun asyncOp(nums: List<Int>, mult: Int, id: String) =
    withContext(Dispatchers.Default) {
        println("1 - $id: $mult times list computing asynchronously")
        Thread.sleep(2000L * mult)
        println("2 - $id: $mult times list computing asynchronously")
        Thread.sleep(1000L * mult)
        nums.map { it * mult }
    }
But this runs it in a blocking way which is not what I wanted. What do you guys think? Where might I be going wrong?
o
(1) you should almost never use GlobalScope, (2) you should use
delay
instead of
Thread.sleep
,
delay
is suspending
👍 1
2
you should probably read https://medium.com/@elizarov/structured-concurrency-722d765aa952 and other kotlin-related coroutine posts
not sure what you're really going for here, but I think this is a better approximation:
Copy code
suspend fun op(nums: List<Int>, mult: Int, id: String): List<Int> {
    println("1 - $id: $mult times list computing asynchronously")
    delay(2000L * mult)
    println("2 - $id: $mult times list computing asynchronously")
    delay(1000L * mult)
    return nums.map { it * mult }
}

suspend fun main() {
    val nums = listOf(1, 2, 3, 4, 5, 6)
    coroutineScope {
        val double = async { op(nums, 2, "first") }
        val triple = async { op(nums, 3, "second") }
        println("Double requested: $double")
        println("Triple requested: $triple")
        println("Waiting...")
        println(awaitAll(triple, double))
    }
}
💯 1
h
They were mainly toy functions to observe some async behavior
The coroutine scope inside the
main
, does that run in a blocking way in your example?
o
what part of it?
h
As in, will the main function finish running before the
coroutineScope
finishes?
o
no, but it does not block, it suspends
h
Yeah alright!
Thanks for your help! That honestly makes a lot more sense
I'll have a look at those resources too! Thanks again
👍 1
Sorry again, but why is using
GlobalScope
not right?
o
it's part of structured concurrency -- I think that blog post explains it well, but as a quick summary it basically means establishing a tree of Jobs, so that when you cancel a Job, all of its children, and its children's children, etc.; all get cancelled, and clean up all resources associated with those jobs
e.g. if you called
coroutineContext[Job]!!.cancel()
in
main()
from our example, after starting the two async ops: in your example they would still proceed to do the
nums.map
code, where as in my code, they immediately stop after resuming from delay (or potentially sooner, don't recall when cancellation is detected by
delay
)
h
Ahh I see! That makes sense actually