Basically, a coroutine is a Thread... a lightweight one, you can run a bunch of coroutines without even notice. They help us to manages our async code and their syntax is simple and easy to understand.
//Basic Example
fun main () {
GlobalScope.launch {
delay(2000)
println("Hello world")
}
Thread.sleep(3000)
}
What is Scope?
Scope create, run, and stop Coroutines, also provides lifecycle methods.
- GlobalScope.launch{}: the scope of these coroutines will be the entire application, so when the app stops the coroutine will stop.
- runBlocking: it will run on the main thread. Runs a new coroutine and blocks the current thread interruptible until its completion.
- CoroutineScope: it will create a new scope and will run until all the inner coroutines finish.
Examples:
fun main () {
runBlocking {
launch {
println("runBlocking ")
}
}
GlobalScope.launch {
println("runBlocking")
}
coroutineScope {
launch {
println("runBlocking")
}
}
}
What is Context?
It's a set of variables and data associated with the coroutines.
- Dispatcher: Specify the thread in which our coroutine will run
- Job: Handle the coroutine lifecycle.
What is a Suspending function?
It's a function that can be run in a Coroutine. So the only difference is that we need to add the 'suspended' key-word at the beginning.
suspend fun myFunction(){
println("Hello, I'm suspended")
}
What is a Job?
A job is the result of the '.launch()', so we can store this in a variable and use it. ( That way we can manipulate the lyfecycle of the corotutine).
val job = GlobalScope.launch {
// Coroutine work...
}
What is a Dispatcher?
Manage in which thread the coroutine will run. We can specify (if we want) in which thread our coroutine will run and switch between threads with the keyword: withContext
Common dispatchers:
launch(Dispatchers.Main) { // Main -- will work with main thread (UI)
println("Main")
}
launch(Dispatchers.IO) { // IO -- will work with IO thread, network or file work
println("IO")
}
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined")
}
launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher
println("Default")
}
withContext :
Let us change the context of our coroutine.
fun main() {
runBlocking {
launch(Dispatchers.Default){
println(“Default context”)
withContext(Dispatchers.IO){
println(“IO context”)
}
println(“Default context”)
}
}
}
What is a Deferred?
A future result of a coroutine, for example, the result for an API call. So we need to wait for this value and we are going to use async.
fun main (){
runBlocking {
val myDefferred = async { suspendfun() }
}
}
suspend fun suspendfun(): Int {
delay(50000)
return 10
}
Top comments (3)
I never quite understood lightweight threads. What makes it light compared to a regular thread?
Basically, every Thread has its own stack. According to the documentation, 64k is the least amount of stack space allowed per thread in the JVM while a simple coroutine in Kotlin occupies only a few dozen bytes of heap memory.
Try to run this example and see It on your own. Then, change it to "normal" Threads and see how your app crash ahah
Every thread has a stack, that's the definition of a thread (well, that plus registers, counters and a couple other things). What makes this lightweight?