Slide 1

Slide 1 text

Svetlana Isakova @sveta_isakova Kotlin Coroutines

Slide 2

Slide 2 text

The Kotlin programming language • Releases: • Kotlin 1.0 - February 2016 • Kotlin 1.1 - March 2017 • Used in JetBrains • Used in Expedia, NBC News, Digital, Netflix, Amex, Pinterest, and others

Slide 3

Slide 3 text

Kotlin Coroutines • the key new feature in Kotlin 1.1 • brings the support of: • async/await, • yield, • and more

Slide 4

Slide 4 text

Coroutine a main routine and a subroutine vs coroutines, which call on each other • the term from 1960s • was used in “The Art of Computer Programming” by Donald Knuth

Slide 5

Slide 5 text

Agenda • motivation: async/await • threads & coroutines • common questions

Slide 6

Slide 6 text

Motivation: async/await

Slide 7

Slide 7 text

Motivation time consuming operation val image = loadImage(url) myUI.setImage(image)

Slide 8

Slide 8 text

Asynchronous computations time consuming operation callback loadImageAsync().whenComplete { image -> panel.setImage(image) }

Slide 9

Slide 9 text

Coroutines suspending call async { val image = loadImageAsync(url).await() myUI.setImage(image) }

Slide 10

Slide 10 text

loadImageAsync().whenComplete { image -> panel.setImage(image) } async { val image = loadImageAsync(url).await() myUI.setImage(image) }

Slide 11

Slide 11 text

C# way async Task LoadImageAsync(String url) { /* do the work */ } Kotlin way fun loadImageAsync() = async { /* do the work */ }

Slide 12

Slide 12 text

C# way async Task ProcessImage(String url) { var image = await LoadImage(url); myUI.SetImage(image); } Kotlin way fun processImage() = async { val image = loadImageAsync().await() myUI.setImage(image) }

Slide 13

Slide 13 text

coroutines async/await Language Library async/await - functions defined in the standard library

Slide 14

Slide 14 text

Threads & Coroutines

Slide 15

Slide 15 text

Asynchronous computations: how?

Slide 16

Slide 16 text

New thread for every computation too expensive

Slide 17

Slide 17 text

Executor • fixed number of threads • adding tasks • but: difficult to manage dependencies

Slide 18

Slide 18 text

Coroutine = “lightweight thread” computation that can be suspended

Slide 19

Slide 19 text

Coroutine = “lightweight thread” computation that can be suspended

Slide 20

Slide 20 text

suspend fun computation that can be suspended

Slide 21

Slide 21 text

Why suspend?

Slide 22

Slide 22 text

Blocking thread idle too expensive

Slide 23

Slide 23 text

thenCombine / zip CompletableFutures / Rx computation in a callback

Slide 24

Slide 24 text

1 2 3 • computation that can be suspended • thread is not blocking! Coroutines

Slide 25

Slide 25 text

suspending call Back to image example fun loadImageAsync() = async { /* do the work */ } fun processImage() = async { val image = loadImageAsync().await() myUI.setImage(image) }

Slide 26

Slide 26 text

fun loadImageAsync(): Deferred = async { /* do the work */ } interface Deferred {
 suspend fun await(): T
 } await is a suspend function

Slide 27

Slide 27 text

fun processImage() = async { val image = loadImageAsync().await() myUI.setImage(image) } await suspends computation loadImageAsync processImage processImage loadImageAsync 1 2 await

Slide 28

Slide 28 text

await suspends computation loadImageAsync processImage 2 loadImageAsync processImage 3 …and continues it when result is ready On which thread?

Slide 29

Slide 29 text

Q: On which thread can the coroutine be continued? A: You specify that.

Slide 30

Slide 30 text

Continue in any thread from thread pool async(CommonPool) {
 ...
 } 1 2 3 3

Slide 31

Slide 31 text

Continue in the UI thread 1 2 3 launch(UI) { ... } UI UI UI

Slide 32

Slide 32 text

Custom executor launch(CustomContext) {
 ...
 } You can launch coroutine in the custom executor

Slide 33

Slide 33 text

fun processImage() = async(UI) {
 val deferred = loadImageAsync()
 // do other work
 val image = deferred.await() showImage(image)
 } Suspension might not happen if the result is already available processImage loadImageAsync await 1 2 processImage loadImageAsync

Slide 34

Slide 34 text

fun overlay(first: Image, second: Image): Image fun overlayAsync() = async(CommonPool) {
 val first = loadImageAsync("green")
 val second = loadImageAsync("red")
 overlay(first.await(), second.await())
 } Image overlay: two asynchronous computations

Slide 35

Slide 35 text

fun overlayAsync() = async(CommonPool) {
 val first = loadImageAsync("green")
 val second = loadImageAsync("red")
 overlay(first.await(), second.await())
 } button.onClick {
 launch(UI) {
 val image = overlayAsync().await()
 showImage(image)
 }
 } Image overlay

Slide 36

Slide 36 text

Common questions

Slide 37

Slide 37 text

Q: What about handling errors (exceptions)? A: await rethrows the exception, so regular try/catch works.

Slide 38

Slide 38 text

await rethrows exceptions val task = async(CommonPool) { ...
 throw MyException()
 }
 try {
 task.await()
 } catch (e: MyException) {
 ...
 } 1 3 throw e catch (e) 2

Slide 39

Slide 39 text

Q: Can I cancel the coroutine? A: Yes (if coroutine is cancellable).

Slide 40

Slide 40 text

val job = async(CommonPool) {
 while (isActive) {
 ...
 }
 }
 job.cancel() Check cancellation explicitly in computation code

Slide 41

Slide 41 text

Library suspend functions like await, delay check for cancellation val job = async(CommonPool) {
 delay(1000)
 task.await()
 }
 job.cancel()

Slide 42

Slide 42 text

You can run the code without cancellation val job = launch(CommonPool) { try { ... } finally { run(NonCancellable) { // this code isn't cancelled } } } job.cancel()

Slide 43

Slide 43 text

Q: Can I define my custom suspend functions? A: Yes.

Slide 44

Slide 44 text

suspend fun login(login: String): UserID
 
 suspend fun loadUserData(userID: UserID): UserData
 
 suspend fun loadImage(id: String): Image Functions that can be suspended

Slide 45

Slide 45 text

suspend fun showUserInfo(name: String) {
 val userID = login(name)
 val data = loadUserData(userID)
 val image = async(CommonPool) {
 loadImage(userData.imageID)
 }
 launch(UI) {
 showData(data)
 showImage(image.await())
 }
 } Using suspend functions

Slide 46

Slide 46 text

Q: Where can I call suspend functions? A: Inside other suspend functions and inside async/launch (in fact: inside suspend lambdas).

Slide 47

Slide 47 text

fun launch( context: CoroutineContext, block: suspend CoroutineScope.() -> Unit ): Job fun async( context: CoroutineContext, block: suspend CoroutineScope.() -> T ) : Deferred suspend lambdas

Slide 48

Slide 48 text

Implementation details

Slide 49

Slide 49 text

suspend fun foo(): Int suspend fun foo(continuation: Continuation): Int Hidden parameter: continuation

Slide 50

Slide 50 text

suspend fun foo(): Int fun fooAsync(): CompletableFuture = future { foo() } fun fooAsync(): Single = rxSingle(CommonPool) { foo() } To call suspend foo from Java wrap it into fooAsync if you have Java 8 if you have RxJava

Slide 51

Slide 51 text

Coroutine body is compiled to a state machine 1 2 3 4 … state 1 state 2

Slide 52

Slide 52 text

Bonus: yield

Slide 53

Slide 53 text

fun load(s: String) = run { println("loading $s"); s } val seq = buildSequence { yield(load("first")) yield(load("second")) } for (s in seq) { println("processing $s") } loading first processing first loading second processing second yield when required

Slide 54

Slide 54 text

coroutines async/await Language Library channels actors kotlinx.coroutines yield

Slide 55

Slide 55 text

kotlinx.coroutines • https://github.com/Kotlin/kotlinx.coroutines/ • Guide to kotlinx.coroutines by example • by Roman Elizarov (@relizarov)

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Have a nice Kotlin!