Introduction to Coroutines Roman Elizarov elizarov at JetBrains - - PowerPoint PPT Presentation

introduction to coroutines
SMART_READER_LITE
LIVE PREVIEW

Introduction to Coroutines Roman Elizarov elizarov at JetBrains - - PowerPoint PPT Presentation

Introduction to Coroutines Roman Elizarov elizarov at JetBrains Asynchronous programming How do we write code that waits for something most of the time? A toy problem fun requestToken(): Token { 1 Kotlin // makes request for a token &


slide-1
SLIDE 1

elizarov at JetBrains Roman Elizarov

Introduction to Coroutines

slide-2
SLIDE 2

Asynchronous programming

slide-3
SLIDE 3

How do we write code that waits for something most of the time?

slide-4
SLIDE 4

A toy problem

Kotlin

fun requestToken(): Token { // makes request for a token & waits return token // returns result when received }

1

slide-5
SLIDE 5

fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { // sends item to the server & waits return post // returns resulting post }

A toy problem

2 Kotlin

slide-6
SLIDE 6

fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { // does some local processing of result }

A toy problem

3 Kotlin

slide-7
SLIDE 7

fun requestToken(): Token { … } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … }

A toy problem

fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

1 2 3

Can be done with threads!

Kotlin

slide-8
SLIDE 8

fun requestToken(): Token { // makes request for a token // blocks the thread waiting for result return token // returns result when received } fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … }

Threads

fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Is anything wrong with it?

slide-9
SLIDE 9

How many threads we can have?

100 🙃

slide-10
SLIDE 10

How many threads we can have?

1000 😆

slide-11
SLIDE 11

How many threads we can have?

10 000 😪

slide-12
SLIDE 12

How many threads we can have?

100 000 😶

slide-13
SLIDE 13

Callbacks to the rescue

Sort of …

slide-14
SLIDE 14

Callbacks: before

fun requestToken(): Token { // makes request for a token & waits return token // returns result when received }

1

slide-15
SLIDE 15

Callbacks: after

fun requestTokenAsync(cb: (Token) -> Unit) { // makes request for a token, invokes callback when done // returns immediately }

1 callback

slide-16
SLIDE 16

Callbacks: before

fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPost(token: Token, item: Item): Post { // sends item to the server & waits return post // returns resulting post }

2

slide-17
SLIDE 17

Callbacks: after

fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { // sends item to the server, invokes callback when done // returns immediately }

2 callback

slide-18
SLIDE 18

Callbacks: before

fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

slide-19
SLIDE 19

Callbacks: after

fun requestTokenAsync(cb: (Token) -> Unit) { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { … } fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync { token -> createPostAsync(token, item) { post -> processPost(post) } } }

aka “callback hell”

This is simplified. Handling exceptions makes it a real mess

slide-20
SLIDE 20

Futures/Promises/Rx to the rescue

Sort of …

slide-21
SLIDE 21

Futures: before

fun requestTokenAsync(cb: (Token) -> Unit) { // makes request for a token, invokes callback when done // returns immediately }

1

slide-22
SLIDE 22

Futures: after

fun requestTokenAsync(): Promise<Token> { // makes request for a token // returns promise for a future result immediately }

1 future

slide-23
SLIDE 23

Futures: before

fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { // sends item to the server, invokes callback when done // returns immediately }

2

slide-24
SLIDE 24

Futures: after

fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> { // sends item to the server // returns promise for a future result immediately }

future 2

slide-25
SLIDE 25

Futures: before

fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync { token -> createPostAsync(token, item) { post -> processPost(post) } } }

slide-26
SLIDE 26

Futures: after

fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } }

Composable & propagates exceptions No nesting indentation

slide-27
SLIDE 27

Futures: after

fun requestTokenAsync(): Promise<Token> { … } fun createPostAsync(token: Token, item: Item): Promise<Post> … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } }

But all those combinators…

slide-28
SLIDE 28

Kotlin coroutines to the rescue

Let’s get real

slide-29
SLIDE 29

Coroutines: before

fun requestTokenAsync(): Promise<Token> { // makes request for a token // returns promise for a future result immediately }

1

slide-30
SLIDE 30

Coroutines: after

suspend fun requestToken(): Token { // makes request for a token & suspends return token // returns result when received }

1

slide-31
SLIDE 31

Coroutines: after

suspend fun requestToken(): Token { // makes request for a token & suspends return token // returns result when received }

1 natural signature

slide-32
SLIDE 32

Coroutines: before

suspend fun requestToken(): Token { … } fun createPostAsync(token: Token, item: Item): Promise<Post> { // sends item to the server // returns promise for a future result immediately }

2

slide-33
SLIDE 33

Coroutines: after

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { // sends item to the server & suspends return post // returns result when received }

2

slide-34
SLIDE 34

Coroutines: before

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } }

slide-35
SLIDE 35

Coroutines: after

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

slide-36
SLIDE 36

Coroutines: after

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Like regular code

slide-37
SLIDE 37

Coroutines: after

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

suspension points

slide-38
SLIDE 38
  • Regular loops

Bonus features

for ((token, item) in list) { createPost(token, item) }

slide-39
SLIDE 39
  • Regular exception handing

Bonus features

try { createPost(token, item) } catch (e: BadTokenException) { … }

slide-40
SLIDE 40
  • Regular higher-order functions
  • forEach, let, apply, repeat, filter, map, use, etc

Bonus features

file.readLines().forEach { line -> createPost(token, line.toItem()) }

Everything like in blocking code

slide-41
SLIDE 41

Suspending functions

slide-42
SLIDE 42

Retrofit async

interface Service { fun createPost(token: Token, item: Item): Call<Post> }

slide-43
SLIDE 43

Retrofit async

interface Service { fun createPost(token: Token, item: Item): Call<Post> }

future

slide-44
SLIDE 44

Retrofit async

interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await()

slide-45
SLIDE 45

Retrofit async

interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await()

natural signature

slide-46
SLIDE 46

Retrofit async

interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await()

Suspending extension function from integration library

slide-47
SLIDE 47

Composition

Beyond sequential

slide-48
SLIDE 48

val post = createPost(token, item)

slide-49
SLIDE 49

Higher-order functions

val post = retryIO { createPost(token, item) }

slide-50
SLIDE 50

Higher-order functions

val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }

slide-51
SLIDE 51

Higher-order functions

val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }

slide-52
SLIDE 52

Higher-order functions

val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }

suspending lambda

slide-53
SLIDE 53

Higher-order functions

val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }

Everything like in blocking code

slide-54
SLIDE 54

Higher-order functions

val post = retryIO { createPost(token, item) } suspend fun <T> retryIO(block: suspend () -> T): T { var curDelay = 1000L // start with 1 sec while (true) { try { return block() } catch (e: IOException) { e.printStackTrace() // log the error } delay(curDelay) curDelay = (curDelay * 2).coerceAtMost(60000L) } }

slide-55
SLIDE 55

Coroutine builders

slide-56
SLIDE 56

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

slide-57
SLIDE 57

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

slide-58
SLIDE 58

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Error: Suspend function 'requestToken' should be called only from a coroutine or another suspend function

slide-59
SLIDE 59

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Can suspend execution

slide-60
SLIDE 60

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Can suspend execution A regular function cannot

slide-61
SLIDE 61

Coroutines revisited

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … } fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Can suspend execution A regular function cannot One cannot simply invoke a suspending function

slide-62
SLIDE 62

Launch

fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } }

coroutine builder

slide-63
SLIDE 63

fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } }

Fire and forget! Returns immediately, coroutine works in background thread pool

slide-64
SLIDE 64

fun postItem(item: Item) { launch { val token = requestToken() val post = createPost(token, item) processPost(post) } }

slide-65
SLIDE 65

fun postItem(item: Item) { launch(UI) { val token = requestToken() val post = createPost(token, item) processPost(post) } }

UI Context

Just specify the context

slide-66
SLIDE 66

fun postItem(item: Item) { launch(UI) { val token = requestToken() val post = createPost(token, item) processPost(post) } }

UI Context

And it gets executed on UI thread

slide-67
SLIDE 67

Where’s the magic of launch?

slide-68
SLIDE 68

fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … }

A regular function

slide-69
SLIDE 69

fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … }

suspending lambda

slide-70
SLIDE 70

fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … }

slide-71
SLIDE 71

async / await

slide-72
SLIDE 72

Kotlin-way

suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }

Kotlin

suspend fun requestToken(): Token { … } suspend fun createPost(token: Token, item: Item): Post { … } fun processPost(post: Post) { … }

slide-73
SLIDE 73

async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); }

Classic-way

C# approach to the same problem (also Python, TS, Dart, coming to JS)

C#

async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }

slide-74
SLIDE 74

async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); }

Classic-way

mark with async C#

async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }

slide-75
SLIDE 75

async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); }

Classic-way

use await to suspend C#

async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }

slide-76
SLIDE 76

async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post); }

Classic-way

C# returns a future

async Task<Token> requestToken() { … } async Task<Post> createPost(Token token, Item item) { … } void processPost(Post post) { … }

slide-77
SLIDE 77

Why no await keyword in Kotlin?

The problem with async

requestToken() VALID –> produces Task<Token> await requestToken() VALID –> produces Token concurrent behavior sequential behavior

C# C# default

slide-78
SLIDE 78

Kotlin suspending functions are designed to imitate sequential behavior by default

Concurrency is hard Concurrency has to be explicit

slide-79
SLIDE 79

Kotlin approach to async

Concurrency where you need it

slide-80
SLIDE 80

Use-case for async

async Task<Image> loadImageAsync(String name) { … }

C#

slide-81
SLIDE 81

Use-case for async

var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); async Task<Image> loadImageAsync(String name) { … } Start multiple operations concurrently

C#

slide-82
SLIDE 82

Use-case for async

var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); var image1 = await promise1; var image2 = await promise2; async Task<Image> loadImageAsync(String name) { … } and then wait for them

C#

slide-83
SLIDE 83

Use-case for async

var result = combineImages(image1, image2);

C#

var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); var image1 = await promise1; var image2 = await promise2; async Task<Image> loadImageAsync(String name) { … }

slide-84
SLIDE 84

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … }

Kotlin

slide-85
SLIDE 85

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … }

Kotlin

A regular function

slide-86
SLIDE 86

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … }

Kotlin’s future type

Kotlin

slide-87
SLIDE 87

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … }

async coroutine builder

Kotlin

slide-88
SLIDE 88

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) Start multiple operations concurrently

Kotlin

slide-89
SLIDE 89

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() and then wait for them

await function

Suspends until deferred is complete

Kotlin

slide-90
SLIDE 90

Kotlin async function

fun loadImageAsync(name: String): Deferred<Image> = async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() val result = combineImages(image1, image2)

Kotlin

slide-91
SLIDE 91

Using async function when needed

suspend fun loadImage(name: String): Image { … } Is defined as suspending function, not async

slide-92
SLIDE 92

Using async function when needed

suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }

slide-93
SLIDE 93

Using async function when needed

suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }

slide-94
SLIDE 94

Using async function when needed

suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }

slide-95
SLIDE 95

Using async function when needed

suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } return combineImages(deferred1.await(), deferred2.await()) }

slide-96
SLIDE 96

Kotlin approach to async

requestToken() VALID –> produces Token async { requestToken() } VALID –> produces Deferred<Token> sequential behavior concurrent behavior

Kotlin Kotlin default

slide-97
SLIDE 97

Coroutines

slide-98
SLIDE 98

What are coroutines conceptually?

slide-99
SLIDE 99

What are coroutines conceptually?

Coroutines are like very light-weight threads

slide-100
SLIDE 100

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

slide-101
SLIDE 101

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

This coroutine builder runs coroutine in the context of invoker thread

slide-102
SLIDE 102

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

slide-103
SLIDE 103

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

slide-104
SLIDE 104

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

Suspends for 1 second

slide-105
SLIDE 105

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

We can join a job just like a thread

slide-106
SLIDE 106

Demo

slide-107
SLIDE 107

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

Try that with 100k threads! Prints 100k dots after one second delay

slide-108
SLIDE 108

fun main(args: Array<String>) = runBlocking<Unit> { val jobs = List(100_000) { launch { delay(1000L) print(".") } } jobs.forEach { it.join() } }

Example

slide-109
SLIDE 109

fun main(args: Array<String>) { val jobs = List(100_000) { thread { Thread.sleep(1000L) print(".") } } jobs.forEach { it.join() } }

Example

slide-110
SLIDE 110

Demo

slide-111
SLIDE 111

fun main(args: Array<String>) { val jobs = List(100_000) { thread { Thread.sleep(1000L) print(".") } } jobs.forEach { it.join() } }

Example

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

slide-112
SLIDE 112

Java interop

slide-113
SLIDE 113

CompletableFuture<Image> loadImageAsync(String name) { … }

Java

slide-114
SLIDE 114

CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2)

Imagine implementing it in Java…

Java

slide-115
SLIDE 115

CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2) { CompletableFuture<Image> future1 = loadImageAsync(name1); CompletableFuture<Image> future2 = loadImageAsync(name2); return future1.thenCompose(image1 -> future2.thenCompose(image2 -> CompletableFuture.supplyAsync(() -> combineImages(image1, image2)))); }

Java

slide-116
SLIDE 116

CompletableFuture<Image> loadImageAsync(String name) { … } CompletableFuture<Image> loadAndCombineAsync(String name1, String name2) { CompletableFuture<Image> future1 = loadImageAsync(name1); CompletableFuture<Image> future2 = loadImageAsync(name2); return future1.thenCompose(image1 -> future2.thenCompose(image2 -> CompletableFuture.supplyAsync(() -> combineImages(image1, image2)))); }

Java

slide-117
SLIDE 117

CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) }

Kotlin Java

slide-118
SLIDE 118

CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) }

Kotlin Java

slide-119
SLIDE 119

CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) }

future coroutine builder

Kotlin Java

slide-120
SLIDE 120

CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) }

Kotlin Java

slide-121
SLIDE 121

CompletableFuture<Image> loadImageAsync(String name) { … } fun loadAndCombineAsync( name1: String, name2: String ): CompletableFuture<Image> = future { val future1 = loadImageAsync(name1) val future2 = loadImageAsync(name2) combineImages(future1.await(), future2.await()) } Extension for Java’s CompletableFuture

Kotlin Java

slide-122
SLIDE 122

Beyond asynchronous code

slide-123
SLIDE 123

Fibonacci sequence

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }

slide-124
SLIDE 124

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }

A coroutine builder with restricted suspension

slide-125
SLIDE 125

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }

A suspending function

slide-126
SLIDE 126

The same building blocks

fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … }

slide-127
SLIDE 127

fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … }

Result is a synchronous sequence

slide-128
SLIDE 128

fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … }

Suspending lambda with receiver

slide-129
SLIDE 129

fun <T> buildSequence( builderAction: suspend SequenceBuilder<T>.() -> Unit ): Sequence<T> { … } @RestrictsSuspension abstract class SequenceBuilder<in T> { abstract suspend fun yield(value: T) }

Coroutine is restricted only to suspending functions defined here

slide-130
SLIDE 130

Synchronous

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator()

slide-131
SLIDE 131

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next())

slide-132
SLIDE 132

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next())

slide-133
SLIDE 133

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1

slide-134
SLIDE 134

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next())

slide-135
SLIDE 135

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next())

slide-136
SLIDE 136

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1

slide-137
SLIDE 137

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1 println(iter.next()) // 2

slide-138
SLIDE 138

val fibonacci = buildSequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } val iter = fibonacci.iterator() println(iter.next()) // 1 println(iter.next()) // 1 println(iter.next()) // 2

Synchronous with invoker

slide-139
SLIDE 139

Library vs Language

slide-140
SLIDE 140

Classic async

async/await generate/yield

Keywords

slide-141
SLIDE 141

Kotlin coroutines

suspend

Modifier

slide-142
SLIDE 142

Kotlin coroutines

Standard library

slide-143
SLIDE 143

Kotlin coroutines

Standard library

kotlinx-coroutines launch, async, runBlocking, future, delay, Job, Deferred, etc

http://github.com/kotlin/kotlinx.coroutines

slide-144
SLIDE 144

Experimental status

Coroutines are here to stay Backwards compatible inside 1.1 & 1.2 To be finalized in the future

slide-145
SLIDE 145

#kotlinconf17

relizarov elizarov at JetBrains Roman Elizarov

Thank you

Any questions?