Daniel Lew
October 05, 2019
1.1k

# Grokking Coroutines

What are coroutines, really?

Given at Kotlin/Everywhere MN.

October 05, 2019

## Transcript

5. ### sequence { var cur = 1 var next = 1

while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }
6. ### –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.”

8. ### Subroutines fun sumSquaredValues(values: List<Int>): Int { return values.sumBy { value

-> square(value) } } fun square(value: Int): Int = value * value

11. ### 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
12. ### 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
13. ### 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
14. ### 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 } }

16. ### Why use coroutines? • Another tool for writing good code

• Two ways to use tool: • Generators • Concurrency

19. ### Blocking Functions fun main() { print(calculateMeaningOfLife()) } fun calculateMeaningOfLife(): Int

{ // Calculates for 7.5 million years, then... return 42 }

return 42 }
21. ### Blocking vs. Nonblocking Blocking Blocked During I/O Non-Blocking Waiting During

I/O Doing other work

{ 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)

26. ### Not Basic 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) }

31. ### fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) suspend fun mySuspendingFunction()

fun main() { ::mySuspendingFunction.startCoroutine( Continuation(EmptyCoroutineContext) { } ) }
32. ### fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) suspend fun mySuspendingFunction()

fun main() { ::mySuspendingFunction.startCoroutine( Continuation(EmptyCoroutineContext) { } ) }
33. ### Continuations public interface Continuation<in T> { public val context: CoroutineContext

public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: Any? )
34. ### Continuations public interface Continuation<in T> { public val context: CoroutineContext

public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: Any? )
35. ### Continuations public interface Continuation<in T> { public val context: CoroutineContext

public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: T?, val exception: Throwable )
36. ### Continuations public interface Continuation<in T> { public val context: CoroutineContext

} fun <T> Continuation<T>.resume(value: T) fun <T> Continuation<T>.resumeWithException(exception: Throwable)
37. ### Coroutines - Under the Hood suspend fun fizzBuzz(): String |

| compiles to… V fun fizzBuzz(continuation: Continuation<String>): Any?
38. ### Coroutines - Under the Hood suspend fun fizzBuzz(): String |

| compiles to… V fun fizzBuzz(continuation: Continuation<String>): Any?

40. ### Callback Hell requestRandomUrl { url -> downloadImage(url) { image ->

displayImage(image) } } myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) } Vs
41. ### Context public interface Continuation<in T> { public val context: CoroutineContext

public fun resumeWith(result: Result<T>) }
42. ### Context public interface Continuation<in T> { public val context: CoroutineContext

public fun resumeWith(result: Result<T>) }
43. ### Coroutine Basics • suspend keyword • Continuations • CoroutineContexts •

…And a couple other minor things not worth mentioning here

45. ### startCoroutine Is Ugly suspend fun mySuspendingFunction() fun main() { ::mySuspendingFunction.startCoroutine(

Continuation(EmptyCoroutineContext) { } ) }
46. ### 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) }
47. ### Starting a Coroutine 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) }
48. ### Launch Patterns myScope.launch { doSomething() } myScope.launch { val result

= doSomething() callback(result) }
49. ### Launch Patterns myScope.launch { doSomething() } myScope.launch { val result

= doSomething() callback(result) }
50. ### CoroutineScope myScope.launch { doSomething() } myScope.launch { val result =

doSomething() callback(result) }

52. ### val job1 = launch { task1() } val job2 =

launch { task2() } val job3 = launch { task3() } ... job1.cancel() job2.cancel() job3.cancel()
53. ### val job1 = launch { task1() } val job2 =

launch { task2() } val job3 = launch { task3() } ... job1.cancel() job2.cancel() job3.cancel()

57. ### class MyActivity : Activity() { private val job = Job()

private val myScope = CoroutineScope(job) /* ...Launch coroutines to your heart's desire... */ override fun onDestroy() { super.onDestroy() job.cancel() } }
58. ### Structured Concurrency • Concurrency with boundaries • Forces you to

do the right thing • Explainer: https://vorpus.org/blog/notes-on-structured-concurrency-or- go-statement-considered-harmful/
59. ### 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 • Sometimes illegal! • Remember CoroutineContext? • Store whatever you want • Idea: store the thread!
60. ### Threading 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) }
61. ### Threading 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) }
62. ### Clarifying Threading • Metaphor: Coroutines are like light-weight threads •

Feature: Kotlin coroutines let you choose thread for coroutines
63. ### Review • Coroutines are suspending functions • Suspending functions are

useful for concurrency • Kotlin stdlib provides coroutine support • Kotlin coroutine library adds practical functions for coroutines

65. ### 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? • Actors - ???