The Async Problem class AsyncExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_async_example) } }
Coroutines are cheap fun main(args: Array) = runBlocking { val jobs = (0 until 100000) .map { launch { delay(5000L) // Fake our long running call print(".") } } // Wait for them to finish... jobs.forEach { it.join() } }
Cancellation fun main(args: Array) = runBlocking { val job = launch { while(true) { println("I'm sleeping...") delay(500L) } } delay(1300L) // delay a bit println("main: I'm tired of waiting!") job.cancel() // cancels the job job.join() // waits for job's completion println("main: Now I can quit.") }
Continuations // The following functions... suspend fun CompletableFuture.await(): T // ...is transformed at compile time to... fun CompletableFuture.await(continuation: Continuation): Any?
Continuations public interface Continuation { public val context: CoroutineContext public fun resume(value: T) public fun resumeWithException(exception: Throwable) }
State machine class extends CoroutineImpl<...> implements Continuation { // The current state of the state machine int label = 0 // local variables of the coroutine A a = null Y y = null void resume(Object data) { if (label == 0) goto L0 if (label == 1) goto L1 if (label == 2) goto L2 else throw IllegalStateException()
State machine L0: // data is expected to be `null` at this invocation a = a() label = 1 data = foo(a).await(this) // 'this' is passed as a continuation if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution L1: // external code has resumed this coroutine passing the result of .await() as data y = (Y) data b() label = 2 data = bar(a, y).await(this) // 'this' is passed as a continuation if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution L2: // external code has resumed this coroutine passing the result of .await() as data Z z = (Z) data c(z) label = -1 // No more steps are allowed return } }
Make it cancelable job = async(UI) { val data: Deferred = bg { loadData() } // Waits until return, or throw CancellationException jobText.append(data.await()) } // in onDestroy() job?.cancel()
At most one at a time suspend fun doJob() { jobText.append("Job started...\n") delay(1000) jobText.append("still working...\n") delay(1000) jobText.append("just a bit more...\n") delay(1000) jobText.append("Job done!\n") }
At most one at a time // Create an actor that reacts to new events, but only one at a time val jobActor = actor(UI) { for (event in channel) doJob() } // Trigger new event on cick jobButton.setOnClickListener { jobActor.offer(Unit) }