SuperAPI AI Directory

kotlin-coroutines

Coding & development

View repository

Updated 10-15 20:31

About this skill

Kotlin Coroutines

Structured concurrency: the parent cancels the children. Flow is a cold stream; StateFlow is state. Language: kotlin. Android ViewModels: android-development / android-unit-testing.

Stack and scope

Item Role
kotlinx.coroutines launch, async, scopes
Flow Streams (map, retry, sharing)
StateFlow / SharedFlow State / UI events
Dispatchers Main, IO, Default

Do not mix in new RxJava if the project already uses Flow — preserve legacy Rx.

Non-negotiable rules

  • Every launch has scope (viewModelScope, lifecycleScope, coroutineScope of the use case). Without GlobalScope.
  • Propagate CoroutineContext / CancellationException upward — do not catch (e: Exception) swallowing cancellation.
  • I/O on Dispatchers.IO (or the injected dispatcher). CPU on Default. UI on Main.
  • withContext to change dispatcher, not launch(IO) nested without need.
  • Timeout on external I/O (withTimeout).
  • Tests: runTest + StandardTestDispatcher — skill android-unit-testing.

Structured concurrency

suspend fun loadProfile(id: UserId): Profile = coroutineScope {
    val user = async { users.fetch(id) }
    val flags = async { flags.fetch(id) }
    Profile(user.await(), flags.await())
}
  • async only with await in the same scope. Avoid async fire-and-forget.
  • supervisorScope when a child can fail without cancelling its siblings — document it.
  • NonCancellable only in the finally of cleanup (closing a resource).

Flow

  • Builder: flow { } with emit. Cold by default.
  • stateIn / shareIn with explicit SharingStarted and scope of the ViewModel.
  • catch at the end of the UI chain; log + state Err.
  • flatMapLatest for queries tied to input (cancels the previous one).
  • Backpressure: do not do heavy work in map without flowOn.
  • SharedFlow for one-shot events (snackbar); do not replace the screen's StateFlow.
val ui: StateFlow<LoadState<Profile>> = id
    .flatMapLatest { repo.profile(it) }
    .map<Profile, LoadState<Profile>> { LoadState.Ready(it) }
    .onStart { emit(LoadState.Loading) }
    .catch { emit(LoadState.Err(it.message ?: "error")) }
    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), LoadState.Loading)

Android

  • viewModelScope + SupervisorJob already included. Do not launch on init work that the test cannot advance.
  • lifecycle.repeatOnLifecycle to collect on the UI — do not collect Flow without lifecycle.
  • Room/DataStore already expose Flow: android-room.

Errors and cancellation

  • CancellationException rethrows. Others: map to the domain.
  • ensureActive() in long loops.
  • Combine Job of the request: a new search cancels the old one.

Anti-patterns

  • GlobalScope.launch
  • runBlocking in production / on Main
  • Flow that emits from Main with I/O inside
  • Converting callback hell into callbackFlow without awaitClose
  • delay as synchronization between product tests
  • SharedFlow(replay=1) mimicking StateFlow

Integration with other skills

  • Kotlin idiom: kotlin
  • Android UI: jetpack-compose / android-development
  • Tests: android-unit-testing
  • Persistence: android-room

Completion criteria

  • Clear scopes; no GlobalScope
  • Cooperative cancellation; timeouts on I/O
  • Flow/StateFlow with explicit started
  • Correct Dispatchers
  • runTest our ViewModels/use cases touched