Advanced

Coroutines

Write asynchronous, non-blocking code with Kotlin coroutines in a sequential style.

What are Coroutines?

Coroutines are Kotlin's solution for asynchronous programming. They let you write async code that looks sequential, avoiding callback hell and complex threading.

Key Concepts

  • suspend function: Can be paused and resumed without blocking the thread
  • CoroutineScope: Defines the lifecycle of coroutines
  • launch: Fire-and-forget — returns a Job
  • async: Returns a value — returns Deferred<T>, await with .await()
  • Dispatchers: Control which thread a coroutine runs on

Coroutine Dispatchers

  • Dispatchers.Main — Android main thread
  • Dispatchers.IO — Network, database, file I/O
  • Dispatchers.Default — CPU-intensive work

Example

kotlin
import kotlinx.coroutines.*

// suspend function - can be paused
suspend fun fetchUser(id: Int): String {
    delay(1000)  // simulate network call (non-blocking!)
    return "User $id"
}

suspend fun fetchPosts(userId: Int): List<String> {
    delay(500)
    return listOf("Post 1", "Post 2", "Post 3")
}

// Sequential execution
suspend fun loadUserData(userId: Int) {
    val user = fetchUser(userId)          // waits 1 second
    val posts = fetchPosts(userId)        // then waits 0.5 seconds
    println("$user has ${posts.size} posts")
}

// Parallel execution with async
suspend fun loadUserDataParallel(userId: Int) {
    coroutineScope {
        val userDeferred = async { fetchUser(userId) }
        val postsDeferred = async { fetchPosts(userId) }

        val user = userDeferred.await()   // both run concurrently!
        val posts = postsDeferred.await()
        println("$user has ${posts.size} posts")
    }
}

// Error handling
suspend fun safeLoad(id: Int): Result<String> {
    return try {
        val user = fetchUser(id)
        Result.success(user)
    } catch (e: Exception) {
        Result.failure(e)
    }
}

fun main() = runBlocking {
    // launch - fire and forget
    val job = launch {
        println("Coroutine started")
        delay(2000)
        println("Coroutine done")
    }

    println("Main continues...")
    job.join()  // wait for job to complete

    // Run both approaches
    loadUserData(1)
    loadUserDataParallel(1)
}
Try it yourself — KOTLIN