Kotlin Cheatsheet
Coroutines & Concurrency
Use this Kotlin reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Setup & core idea
Coroutines live in kotlinx-coroutines-core (not the bare stdlib):
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
}A suspend function can pause without blocking its thread; it may only be called from another suspend function or a coroutine builder.
import kotlinx.coroutines.* suspend fun fetchUser(id: Int): String { delay(100) // suspends, doesn't block return "user-$id" } fun main() = runBlocking { // bridge blocking world → coroutines println(fetchUser(1)) }
Builders: launch, async, coroutineScope
fun main() = runBlocking { val job: Job = launch { // fire-and-forget, returns Job delay(50); println("side effect") } val deferred: Deferred<Int> = async { compute() } // returns a value println(deferred.await()) job.join() } // Parallel decomposition — both run concurrently, fail together: suspend fun loadDashboard(): Dashboard = coroutineScope { val user = async { fetchUser() } val posts = async { fetchPosts() } Dashboard(user.await(), posts.await()) }
coroutineScope { } waits for all children and cancels siblings if one fails — that is structured concurrency. supervisorScope { } lets siblings survive a child's failure.
Dispatchers & withContext
val data = withContext(Dispatchers.IO) { // switch thread pool for the block File("big.json").readText() }
| Dispatcher | Use for |
|---|---|
Dispatchers.Default | CPU-bound work (cores-sized pool) |
Dispatchers.IO | blocking IO — files, JDBC, legacy HTTP |
Dispatchers.Main | UI thread (Android/Compose/Swing artifact) |
Dispatchers.Unconfined | rarely — tests/edge cases |
withContext is the idiomatic "run this on X and give me the result"; never wrap it around launch.
Cancellation & timeouts
val job = launch { while (isActive) { // cooperative: check or call suspend fns step() } } job.cancelAndJoin() val result = withTimeoutOrNull(2_000) { slowCall() } // null on timeout withTimeout(2_000) { slowCall() } // throws TimeoutCancellationException
Cancellation is cooperative — delay, yield(), and most kotlinx suspend calls check it; tight CPU loops must check isActive/ensureActive(). Catch blocks must rethrow CancellationException, or the coroutine becomes uncancellable:
try { work() } catch (e: CancellationException) { throw e } catch (e: Exception) { log(e) }
Error handling
// async: exception surfaces at await() val d = async { mightThrow() } val v = runCatching { d.await() }.getOrNull() // launch: exception propagates to the scope/parent val handler = CoroutineExceptionHandler { _, e -> log(e) } val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler) scope.launch { risky() } // failure won't kill sibling coroutines
Flow (cold async streams)
import kotlinx.coroutines.flow.* fun ticks(): Flow<Int> = flow { // cold: runs per collector var i = 0 while (true) { emit(i++); delay(1_000) } } ticks() .map { it * 2 } .filter { it % 4 == 0 } .take(5) .flowOn(Dispatchers.Default) // upstream context .collect { println(it) } // terminal, suspends flowOf(1, 2, 3).toList() list.asFlow().first()
Hot state for UI/app state:
val state = MutableStateFlow(0) // always has a value, conflated state.value++ // read/write without suspending state.collect { render(it) } val events = MutableSharedFlow<Event>() // broadcast, no initial value events.emit(Event.Click)