Documentation

Coroutines

Kotlin's coroutines, with the ceremony removed: io, cpu and main for picking a thread, and $scope() for work you need to be able to cancel.

suspend functions

suspend works exactly as it does in Kotlin:

suspend fun loadData() { … }

A suspend function in a component's script is one of the things that promotes it to a ViewModel, so its work survives rotation. See State & Reactivity.

See it happen

Add a suspend fun and watch the whole file grow a ViewModel.

Loader.wh
var count = 0

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

No suspend function, so count lives in remember — simple, but a rotation loses it.

Choosing a thread

Three blocks, named for what the work is rather than which dispatcher backs it:

<Button onClick={() => io { loadData() }}>Load</Button>
<Button onClick={() => cpu { process() }}>Process</Button>
<Button onClick={() => main { update() }}>Update</Button>
BlockDispatcherFor
ioDispatchers.IONetwork, disk, database — work that waits
cpuDispatchers.DefaultParsing, sorting, image work — work that computes
mainDispatchers.MainTouching UI from inside background work

Each compiles to a launch on a scope tied to the composable:

dispatcherScope.launch(Dispatchers.IO) { loadData() }
These launch and return — they do not block the caller and they do not hand you a result. To use a value, assign to state inside the block and let the recomposition pick it up.

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.

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