Kotlin Coroutines

Asynchronous programming made simple and efficient

⚑ What are Kotlin Coroutines?

Coroutines enable asynchronous programming by allowing functions to pause and resume execution without blocking threads. They make concurrent code simple, readable, and efficient for handling long-running operations like network requests and file processing.


import kotlinx.coroutines.*

// Simple coroutine example
suspend fun fetchData(): String {
    delay(1000)  // Simulates network delay
    return "Data loaded!"
}

// Usage in main function
runBlocking {
    println("Starting...")
    val data = fetchData()
    println(data)
}
                                    

Output:

Starting...

Data loaded!

Coroutine Concepts

⏸️

Suspend Functions

Functions that can pause and resume

suspend fun doWork()
πŸš€

Launch

Start coroutines without blocking

launch { /* async work */ }
πŸ”„

Async/Await

Concurrent execution with results

async { computation() }
⏰

Delay

Non-blocking pause execution

delay(1000)

πŸ”Ή Basic Coroutines

Creating and running simple coroutines:

import kotlinx.coroutines.*

fun basicCoroutineExample() = runBlocking {
    println("Main program starts: ${Thread.currentThread().name}")
    
    // Launch a coroutine
    launch {
        delay(1000)
        println("World! ${Thread.currentThread().name}")
    }
    
    println("Hello, ${Thread.currentThread().name}")
    delay(2000)  // Keep main alive
    println("Main program ends")
}

// Suspend function example
suspend fun doSomethingUseful(): Int {
    println("Doing something useful...")
    delay(1000)  // Pretend we are doing something useful here
    return 42
}

fun suspendFunctionExample() = runBlocking {
    val result = doSomethingUseful()
    println("Result: $result")
}

basicCoroutineExample()
suspendFunctionExample()

Output:

Main program starts: main

Hello, main

World! main

Main program ends

Doing something useful...

Result: 42

πŸ”Ή Async and Await

Run multiple operations concurrently:

import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

suspend fun fetchUserData(userId: Int): String {
    delay(1000)  // Simulate network call
    return "User$userId data"
}

suspend fun fetchUserPosts(userId: Int): String {
    delay(1500)  // Simulate network call
    return "User$userId posts"
}

fun concurrentExample() = runBlocking {
    val time = measureTimeMillis {
        // Sequential execution (slow)
        println("=== Sequential ===")
        val userData = fetchUserData(1)
        val userPosts = fetchUserPosts(1)
        println("$userData and $userPosts")
    }
    println("Sequential took: ${time}ms")
    
    val concurrentTime = measureTimeMillis {
        // Concurrent execution (fast)
        println("=== Concurrent ===")
        val userDataDeferred = async { fetchUserData(2) }
        val userPostsDeferred = async { fetchUserPosts(2) }
        
        val userData = userDataDeferred.await()
        val userPosts = userPostsDeferred.await()
        println("$userData and $userPosts")
    }
    println("Concurrent took: ${concurrentTime}ms")
}

concurrentExample()

Output:

=== Sequential ===

User1 data and User1 posts

Sequential took: 2500ms

=== Concurrent ===

User2 data and User2 posts

Concurrent took: 1500ms

πŸ”Ή Coroutine Scopes

Managing coroutine lifecycles with scopes:

import kotlinx.coroutines.*

fun scopeExample() = runBlocking {
    println("=== Coroutine Scope Example ===")
    
    // Create a custom scope
    val job = Job()
    val scope = CoroutineScope(Dispatchers.Default + job)
    
    // Launch multiple coroutines in the scope
    repeat(3) { i ->
        scope.launch {
            delay(100L * i)
            println("Coroutine $i completed")
        }
    }
    
    delay(500)  // Wait for coroutines to complete
    
    // Cancel all coroutines in the scope
    println("Cancelling scope...")
    job.cancel()
    
    // Try to launch after cancellation
    try {
        scope.launch {
            println("This won't execute")
        }
    } catch (e: Exception) {
        println("Cannot launch in cancelled scope")
    }
}

// Exception handling in coroutines
fun exceptionHandlingExample() = runBlocking {
    println("=== Exception Handling ===")
    
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Caught exception: ${exception.message}")
    }
    
    val job = launch(handler) {
        delay(100)
        throw RuntimeException("Something went wrong!")
    }
    
    job.join()
    println("Program continues...")
}

scopeExample()
exceptionHandlingExample()

Output:

=== Coroutine Scope Example ===

Coroutine 0 completed

Coroutine 1 completed

Coroutine 2 completed

Cancelling scope...

=== Exception Handling ===

Caught exception: Something went wrong!

Program continues...

🧠 Test Your Knowledge

What keyword is used to define a function that can be paused and resumed?