Documentation

Coroutines

Kotlin's coroutines, with one rule: call a suspend function from anywhere, and it runs. Nothing wraps it, and nothing chooses a thread for you unless you ask.

suspend functions

suspend works exactly as it does in Kotlin:

suspend fun loadData() { … }

Call it from a handler, $onMount, or another function, and the compiler launches it wherever it's referenced:

suspend fun loadData() { … }

<Button onClick={loadData}>Load</Button>

See it happen

Add a suspend fun and watch the click handler launch it.

Loader.wh
import $lib.components.ui.Button

var count = $state(0)

<Button onClick={() => count++}>Increment</Button>
Kotlin waiting
Scroll into view to compile.

count is an Int, so it lives in rememberSaveable and survives a rotation.

Naming a thread yourself

For CPU-bound work — parsing, sorting, image processing — name the dispatcher directly, with Kotlin's own API:

var processed: Data? = $state(null)

<Button onClick={() => process()}>Process</Button>

suspend fun process() {
  processed = withContext(Dispatchers.Default) {
    processLargeDataset()
  }
}

There is nothing else to name. A suspend call is IO-bound or CPU-bound the same way it would be in a plain Kotlin app — withContext is how you say which, and it is the only spelling.

Cancellable work

$scope() gives you a scope you hold onto, for work a user can start and then change their mind about:

val uploadScope = $scope()

<Button onClick={() => uploadScope.launch { upload() }}>Upload</Button>
<Button onClick={() => uploadScope.cancel()}>Cancel</Button>

It compiles to rememberCoroutineScope(), so it is scoped to the composable and anything still running is cancelled when the screen leaves. Use it for a choice, not a thread — an ordinary suspend call needs no scope at all.

Work that should outlive the screen

A coroutine dies with the composable that launched it. Uploading a photo, syncing a feed, or anything that must finish whether or not the user stays on the screen belongs in a worker instead, which Android schedules and retries:

$dispatch(api.upload, name: "upload-$id", constraints: ["wifi"])

See $dispatch / $periodic.

A broadcast receiver is stricter still — it refuses runBlocking, Thread.sleep and suspend outright, because it has sixty seconds before Android reports an ANR.

See Also