Skip to content

Instantly share code, notes, and snippets.

@rajeshsahu09
Last active December 14, 2024 19:44
Show Gist options
  • Save rajeshsahu09/ec2d491d9d09a7a5f8a4ad53d7a59f11 to your computer and use it in GitHub Desktop.
Save rajeshsahu09/ec2d491d9d09a7a5f8a4ad53d7a59f11 to your computer and use it in GitHub Desktop.
Basics

Kotlin Coroutines Guide

1. What is a Coroutine?

  • Coroutines are lightweight cooperative concurrent mechanisms.
  • They have the ability to pause and resume a function without blocking the thread.

2. Why Choose Coroutines Over Threads?

  • Blocking a thread is memory-intensive.
  • Context switching between threads is expensive.
  • During I/O operations, a thread remains idle while waiting for a response.
  • The number of threads that can be created depends on available memory.

Example:

import kotlinx.coroutines.*

fun main() = runBlocking { // Coroutine builder
    launch { // Launch a new coroutine
        delay(1000L) // Non-blocking delay
        println("World!")
    }
    println("Hello, ")
}
// Output:
// Hello, 
// World!

3. Coroutine Components

3.1 Scope

Defines the lifecycle of a coroutine:

  • GlobalScope: Lives throughout the application lifecycle.
GlobalScope.launch {
    println("Running in GlobalScope")
}
// Output:
// Running in GlobalScope
  • CoroutineScope: Custom scope with specific lifecycle.
val myScope = CoroutineScope(Dispatchers.Default)
myScope.launch {
    println("Running in custom CoroutineScope")
}
// Output:
// Running in custom CoroutineScope
  • lifecycleScope: Tied to the lifecycle of Android components (e.g., Activity, Service).
lifecycleScope.launch {
    fetchData()
}
suspend fun fetchData() {
    delay(2000) // Simulate network call
    println("Data fetched!")
}
// Output:
// Data fetched!
  • viewModelScope: Scoped to ViewModel lifecycle in Android.
viewModelScope.launch {
    delay(2000) // Simulate network call
    println("Data fetched in ViewModel!")
}
// Output:
// Data fetched in ViewModel!
  • Scope Cancellation: Cancel all coroutines in a scope.
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
    val child = launch {
        println("Child coroutine started")
        delay(1000)
        println("Child coroutine finished")
    }
    delay(500)
    println("Cancelling scope")
    scope.cancel()
}
// Output:
// Child coroutine started
// Cancelling scope
  • Custom Scope: Create with CoroutineScope(Dispatchers.IO + SupervisorJob()).

3.2 Coroutine Context

  • Dispatchers:
    • Main: For UI or log-related tasks.
    • IO: Optimized for network or file operations.
    • Default: For CPU-intensive tasks.
    • Unconfined: Starts in the current thread but may switch threads.
launch(Dispatchers.Default) {
    println("Default Dispatcher: Running in thread ${Thread.currentThread().name}")
}
// Output:
// Default Dispatcher: Running in thread DefaultDispatcher-worker-1

3.3 Suspend Functions

  • Mark a function with suspend to indicate it can be paused.
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
// Output:
// World!

3.4 delay

  • Delays a coroutine without blocking the thread.

4. Coroutine Builders

4.1 launch

  • Fire-and-forget style coroutine.
runBlocking {
    val job = CoroutineScope(Dispatchers.IO).launch {
        println("Coroutine launched")
    }
    job.join()
}
// Output:
// Coroutine launched

4.2 async

  • Coroutine that returns a result.
suspend fun computeSomething(): Int {
    delay(500)
    return 42
}

runBlocking {
    val result = CoroutineScope(Dispatchers.IO).async {
        computeSomething()
    }
    println("Result: ${result.await()}")
}
// Output:
// Result: 42

4.3 withContext

suspend fun fetchData(): String = withContext(Dispatchers.IO) {
    delay(1000)
    "Data fetched!"
}

runBlocking {
    println(fetchData())
}
// Output:
// Data fetched!

4.4 runBlocking

runBlocking {
    println("Running a blocking coroutine")
}
// Output:
// Running a blocking coroutine

4.5 coroutineScope

suspend fun fetchData() = coroutineScope {
    launch {
        delay(1000L)
        println("Fetching User Data")
    }
    launch {
        delay(500L)
        println("Fetching Posts Data")
    }
}

runBlocking {
    fetchData()
}
// Output:
// Fetching Posts Data
// Fetching User Data

5. Structured Concurrency

Ensures that parent and child coroutines follow a defined lifecycle.

  • It's a builder paradigm. Start - run - complete should be in order.
suspend fun fetchData() = coroutineScope {
    launch {
        delay(1000L)
        println("Fetching User Data")
    }
    launch {
        delay(500L)
        println("Fetching Posts Data")
    }
}

runBlocking {
    fetchData()
    println("All data fetched!")
}
// Output:
// Fetching Posts Data
// Fetching User Data
// All data fetched!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment