–The Design of Everyday Things “A good conceptual model allows us to predict the effects of our actions. Without a good model we operate by rote, blindly; we do operations as we were told to do them; we can’t fully appreciate why, what effects to expect, or what to do if things go wrong.”
val iterator = sequence.iterator() while (iterator.hasNext()) { val next = iterator.next() println(next) } sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } Consumer Producer
val iterator = sequence.iterator() while (iterator.hasNext()) { val next = iterator.next() println(next) } sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } Consumer Producer
class Fibonacci { var cur = 1 var next = 1 fun next(): Int { val toReturn = cur val tmp = cur + next cur = next next = tmp return toReturn } } sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } Versus
Multiple Suspend Points sequence { yield(1) // first Fibonacci number var cur = 1 var next = 1 while (true) { yield(next) // next Fibonacci number val tmp = cur + next cur = next next = tmp } }
Blocking Functions fun main() { print(calculateMeaningOfLife()) } fun calculateMeaningOfLife(): Int { // Calculates for 7.5 million years, then... return 42 }
Two Tasks, One Thread suspend fun task(name: String, delay: Long) { joinAll( async { doSomething("First", 10) }, async { doSomething("Second", 5) } ) } suspend fun doSomething(name: String, delay: Long) { println("$name START (${Thread.currentThread().name})") delay(delay) println("$name END (${Thread.currentThread().name})") } First START (main) Second START (main) Second END (main) First END (main)
Continuations public interface Continuation { public val context: CoroutineContext public fun resumeWith(result: Result) } class Result constructor( val value: Any? )
Continuations public interface Continuation { public val context: CoroutineContext public fun resumeWith(result: Result) } class Result constructor( val value: Any? )
Continuations public interface Continuation { public val context: CoroutineContext public fun resumeWith(result: Result) } class Result constructor( val value: T?, val exception: Throwable )
Continuations public interface Continuation { public val context: CoroutineContext } fun Continuation.resume(value: T) fun Continuation.resumeWithException(exception: Throwable)
startCoroutine Is Ugly suspend fun mySuspendingFunction() fun main() { ::mySuspendingFunction.startCoroutine( Continuation(EmptyCoroutineContext) { } ) }
What We Want suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … } suspend fun downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
What about threads? • Don’t want to run everything on a single thread • Writing everything non-blocking is exhausting • Can’t take advantage of multiple cores
But Wait, There’s More! • Compatibility - How do you use asynchronous non-coroutine code with coroutines? • Composition - How do you concurrently execute multiple suspending functions?
• Cancellation - How do coroutines actually stop?
• Exception handling - What happens when something goes wrong?
• Flow - How can coroutines return multiple values asynchronously? • Channels - How do coroutines communicate between threads safely?