Slide 1

Slide 1 text

Kotlin Coroutines

Slide 2

Slide 2 text

Kotlin Coroutines • the key new feature in Kotlin 1.1 • simplify asynchronous programming

Slide 3

Slide 3 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 4

Slide 4 text

Motivation: async/await

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Solution 1: callbacks loadImageAsync().whenComplete { image -> runOnUiThread { setImage(image) } }

Slide 7

Slide 7 text

Solution 2: async/await async(UI) { val image = loadImageAsync(url).await() setImage(image) }

Slide 8

Slide 8 text

val image = loadImageAsync(url).await() setImage(image) val image = loadImage(url) setImage(image) No callbacks!

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Threads & Coroutines

Slide 12

Slide 12 text

Coroutine is similar to a thread Thread: • a sequence of instructions Multiple threads: • can be executed concurrently • share resources such as memory Coroutine: Multiple coroutines:

Slide 13

Slide 13 text

Coroutine = “lightweight thread” Thread Coroutine

Slide 14

Slide 14 text

Coroutine computation that can be suspended computation thread

Slide 15

Slide 15 text

Coroutine computation that can be suspended

Slide 16

Slide 16 text

Why suspend?

Slide 17

Slide 17 text

Asynchronous computations: how?

Slide 18

Slide 18 text

New thread for every computation simple & straightforward, but too expensive

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Example: waiting for two asynchronous computations to complete

Slide 21

Slide 21 text

thenCombine / zip CompletableFutures / RxJava: managing dependencies the rest of computation in a callback

Slide 22

Slide 22 text

Stepping back: what’s wrong with blocking thread? idle too expensive

Slide 23

Slide 23 text

Stepping back: what’s wrong with blocking UI thread? blocked user is blocked UI

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

suspend function computation that can be suspended

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

async starts new computation fun loadImageAsync() = async { /* do the work */ } loadImageAsync

Slide 29

Slide 29 text

suspending call fun processImage() = async { val image = loadImageAsync().await() setImage(image) } await suspends computation

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

await suspends computation loadImageAsync processImage 2 loadImageAsync processImage 3 …and continues it when result is ready fun processImage() = async { val deferred = loadImageAsync() val image = deferred.await() setImage(image) }

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

CommonPool is used by default async(CommonPool) {
 ...
 } async { ... } is the same as

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Programming with suspend functions

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Example: simple consecutive logic fun login(credentials: Credentials): UserID fun loadUserData(userID: UserID): UserData fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }

Slide 45

Slide 45 text

Rewrite with CompletableFuture fun loginAsync(credentials: Credentials): CompletableFuture fun loadUserDataAsync(userID: UserID): CompletableFuture fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { loginAsync(credentials) .thenCompose { loadUserDataAsync(it) } .thenAccept { showData(it) } }

Slide 46

Slide 46 text

Rewrite with async/await fun login(credentials: Credentials): Deferred fun loadUserData(userID: UserID): Deferred fun showData(data: UserData) fun showUserInfo(credentials: Credentials) = async { val userID = login(credentials).await() val userData = loadUserData(userID).await() showData(userData) }

Slide 47

Slide 47 text

Rewrite with suspend functions suspend fun login(credentials: Credentials): UserID suspend fun loadUserData(userID: UserID): UserData fun showData(data: UserData) suspend fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }

Slide 48

Slide 48 text

Q: Where can I call suspend functions? A: Inside other suspend functions and inside async, launch, etc. (so-called, coroutine builders).

Slide 49

Slide 49 text

val deferred = async { … } deferred.await() val job = launch { … } job.join() runBlocking { … } Coroutine Builders start a coroutine

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

fun showUserInfo(credentials: Credentials) = launch(UI) {
 val userID = login(credentials)
 val data = loadUserData(userID)
 val image = async {
 loadImage(data.imageID)
 }
 showData(data)
 showImage(image.await())
 } Nested coroutines

Slide 52

Slide 52 text

Implementation details

Slide 53

Slide 53 text

suspend fun foo(): Int suspend fun foo(continuation: Continuation): Int Hidden parameter: continuation Continuation is a generic callback interface: public interface Continuation { public val context: CoroutineContext public fun resume(value: T) public fun resumeWithException(exception: Throwable) }

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Coroutines Regular world

Slide 56

Slide 56 text

Q: Can I call a suspend function from Java? suspend fun foo(continuation: Continuation): Int A: You don’t want to call it directly (because of Continuation parameter).

Slide 57

Slide 57 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 58

Slide 58 text

fun loadUserData(userId: UserID, callback: (UserData?, Throwable?) -> Unit) suspend fun loadUserData(userId: UserID): UserData { return suspendCoroutine { continuation -> loadUserData(userId) { data, exception -> if (data != null) { continuation.resume(data) } else { continuation.resumeWithException(exception!!) } } } } Wrapping third-party callback-based API

Slide 59

Slide 59 text

fun loadUserDataRx(userId: UserID): Single suspend fun loadUserData(userId: UserID): UserData { return suspendCoroutine { continuation -> loadUserDataRx(userId) .doOnSuccess { continuation.resume(it) } .doOnError { continuation.resumeWithException(it) } .subscribe() } } Wrapping a function returning rx.Single or just loadUserDataRx(userId).await()

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Experimental status of Coroutines • We want the community to try it! • Migration aids will be provided • Old code will continue to work via the support library

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

• “Deep dive into coroutines on JVM” by Roman Elizarov

Slide 66

Slide 66 text

Copyright © 2017 https://github.com/JetBrains/kotlin-workshop