Daniel Lew
October 05, 2019
840

# Grokking Coroutines

What are coroutines, really?

Given at Kotlin/Everywhere MN.

October 05, 2019

## Transcript

1. Grokking Coroutines
@danlew42

2. Versus
https://www.ﬂickr.com/photos/[email protected]/262930800/

3. “Essentially, coroutines are light-weight
~Kotlin Documentation

4. suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}

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.”

7. What are coroutines?

8. Subroutines
fun sumSquaredValues(values: List): Int {
return values.sumBy { value ->
square(value)
}
}
fun square(value: Int): Int = value * value

9. Routine Subroutine

10. Coroutine Coroutine

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
}
}
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
}
}

15. Why use coroutines?

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

• Two ways to use tool:

• Generators

• Concurrency

17. https://www.ﬂickr.com/photos/[email protected]/12547460924

19. Blocking Functions
fun main() {
print(calculateMeaningOfLife())
}
fun calculateMeaningOfLife(): Int {
// Calculates for 7.5 million years, then...
return 42
}

20. Non-Blocking Function
suspend fun calculateMeaningOfLife(): Int {
delay(7.5 million years)
return 42
}

21. Blocking vs. Nonblocking
Blocking Blocked During I/O
Non-Blocking
Waiting During I/O
Doing other work

suspend fun task(name: String, delay: Long) {
joinAll(
async { doSomething("First", 10) },
async { doSomething("Second", 5) }
)
}
suspend fun doSomething(name: String, delay: Long) {
delay(delay)
}
First START (main)
Second START (main)
Second END (main)
First END (main)

24. Coroutines *are* like light-

25. Kotlin Coroutine Basics

26. Not Basic
suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}

27. kotlin.coroutines
(stdlib)
kotlinx.coroutines
(library)

28. kotlin.coroutines
(stdlib)
kotlinx.coroutines
(library)

29. Suspend Keyword

30. How the [email protected]#\$ do you start a
coroutine?

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

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

33. Continuations
public interface Continuation {
public val context: CoroutineContext
public fun resumeWith(result: Result)
}
class Result constructor(
val value: Any?
)

34. Continuations
public interface Continuation {
public val context: CoroutineContext
public fun resumeWith(result: Result)
}
class Result constructor(
val value: Any?
)

35. Continuations
public interface Continuation {
public val context: CoroutineContext
public fun resumeWith(result: Result)
}
class Result constructor(
val value: T?,
val exception: Throwable
)

36. Continuations
public interface Continuation {
public val context: CoroutineContext
}
fun Continuation.resume(value: T)
fun Continuation.resumeWithException(exception: Throwable)

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

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

39. They’re Just Callbacks!
Coroutines Callbacks

40. Callback Hell
requestRandomUrl { url ->
displayImage(image)
}
}
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}
Vs

41. Context
public interface Continuation {
public val context: CoroutineContext
public fun resumeWith(result: Result)
}

42. Context
public interface Continuation {
public val context: CoroutineContext
public fun resumeWith(result: Result)
}

43. Coroutine Basics
• suspend keyword

• Continuations

• CoroutineContexts

• …And a couple other minor things not worth mentioning here

44. Kotlin Coroutine Library

45. startCoroutine Is Ugly
suspend fun mySuspendingFunction()
fun main() {
::mySuspendingFunction.startCoroutine(
Continuation(EmptyCoroutineContext) { }
)
}

46. What We Want
suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}

47. Starting a Coroutine
suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
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()

54. val job = Job()
val myScope = CoroutineScope(job)
...
...
job.cancel()

55. val job = Job()
val myScope = CoroutineScope(job)
...
...
job.cancel()

56. val job = Job()
val myScope = CoroutineScope(job)
...
...
job.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/

• 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

suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}

suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }
fun displayImage(image: Image)
myScope.launch {
val url = requestRandomUrl()
displayImage(image)
}

• 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

64. What Now?

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 - ???