Introduction to Coroutines @ KotlinConf 2017

145
elizarov at JetBrains Roman Elizarov Introduction to Coroutines

Transcript of Introduction to Coroutines @ KotlinConf 2017

Page 1: Introduction to Coroutines @ KotlinConf 2017

elizarov at JetBrainsRoman Elizarov

Introduction to Coroutines

Page 2: Introduction to Coroutines @ KotlinConf 2017

Asynchronous programming

Page 3: Introduction to Coroutines @ KotlinConf 2017

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

Page 4: Introduction to Coroutines @ KotlinConf 2017

A toy problemKotlin fun requestToken(): Token {

// makes request for a token & waitsreturn token // returns result when received

}

1

Page 5: Introduction to Coroutines @ KotlinConf 2017

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

// sends item to the server & waits return post // returns resulting post

}

A toy problem2

Kotlin

Page 6: Introduction to Coroutines @ KotlinConf 2017

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

Page 7: Introduction to Coroutines @ KotlinConf 2017

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)

}

123

Can be done with threads!

Kotlin

Page 8: Introduction to Coroutines @ KotlinConf 2017

fun requestToken(): Token {// makes request for a token // blocks the thread waiting for resultreturn 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?

Page 9: Introduction to Coroutines @ KotlinConf 2017

How many threads we can have?

100 🙂

Page 10: Introduction to Coroutines @ KotlinConf 2017

How many threads we can have?

1000 😅

Page 11: Introduction to Coroutines @ KotlinConf 2017

How many threads we can have?

10 000 😩

Page 12: Introduction to Coroutines @ KotlinConf 2017

How many threads we can have?

100 000 😵

Page 13: Introduction to Coroutines @ KotlinConf 2017

Callbacks to the rescueSort of …

Page 14: Introduction to Coroutines @ KotlinConf 2017

Callbacks: beforefun requestToken(): Token {

// makes request for a token & waitsreturn token // returns result when received

}

1

Page 15: Introduction to Coroutines @ KotlinConf 2017

Callbacks: afterfun requestTokenAsync(cb: (Token) -> Unit) {

// makes request for a token, invokes callback when done// returns immediately

}

1callback

Page 16: Introduction to Coroutines @ KotlinConf 2017

Callbacks: beforefun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPost(token: Token, item: Item): Post {

// sends item to the server & waits return post // returns resulting post

}

2

Page 17: Introduction to Coroutines @ KotlinConf 2017

Callbacks: afterfun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPostAsync(token: Token, item: Item,

cb: (Post) -> Unit) {// sends item to the server, invokes callback when done// returns immediately

}

2callback

Page 18: Introduction to Coroutines @ KotlinConf 2017

Callbacks: beforefun 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)

}

Page 19: Introduction to Coroutines @ KotlinConf 2017

Callbacks: afterfun 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

Page 20: Introduction to Coroutines @ KotlinConf 2017

Futures/Promises/Rx to the rescue

Sort of …

Page 21: Introduction to Coroutines @ KotlinConf 2017

Futures: beforefun requestTokenAsync(cb: (Token) -> Unit) {

// makes request for a token, invokes callback when done// returns immediately

}

1

Page 22: Introduction to Coroutines @ KotlinConf 2017

Futures: afterfun requestTokenAsync(): Promise<Token> {

// makes request for a token// returns promise for a future result immediately

}

1future

Page 23: Introduction to Coroutines @ KotlinConf 2017

Futures: beforefun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item,

cb: (Post) -> Unit) {// sends item to the server, invokes callback when done// returns immediately

}

2

Page 24: Introduction to Coroutines @ KotlinConf 2017

Futures: afterfun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item): Promise<Post> {

// sends item to the server // returns promise for a future result immediately

}

future2

Page 25: Introduction to Coroutines @ KotlinConf 2017

Futures: beforefun 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)

}}

}

Page 26: Introduction to Coroutines @ KotlinConf 2017

Futures: afterfun 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

Page 27: Introduction to Coroutines @ KotlinConf 2017

Futures: afterfun 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…

Page 28: Introduction to Coroutines @ KotlinConf 2017

Kotlin coroutines to the rescueLet’s get real

Page 29: Introduction to Coroutines @ KotlinConf 2017

Coroutines: beforefun requestTokenAsync(): Promise<Token> {

// makes request for a token// returns promise for a future result immediately

}

1

Page 30: Introduction to Coroutines @ KotlinConf 2017

Coroutines: aftersuspend fun requestToken(): Token {

// makes request for a token & suspendsreturn token // returns result when received

}

1

Page 31: Introduction to Coroutines @ KotlinConf 2017

Coroutines: aftersuspend fun requestToken(): Token {

// makes request for a token & suspendsreturn token // returns result when received

}

1natural signature

Page 32: Introduction to Coroutines @ KotlinConf 2017

Coroutines: beforesuspend fun requestToken(): Token { … }fun createPostAsync(token: Token, item: Item): Promise<Post> {

// sends item to the server // returns promise for a future result immediately

}

2

Page 33: Introduction to Coroutines @ KotlinConf 2017

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

// sends item to the server & suspendsreturn post // returns result when received

}

2

Page 34: Introduction to Coroutines @ KotlinConf 2017

Coroutines: beforesuspend 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) }}

Page 35: Introduction to Coroutines @ KotlinConf 2017

Coroutines: aftersuspend 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)

}

Page 36: Introduction to Coroutines @ KotlinConf 2017

Coroutines: aftersuspend 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

Page 37: Introduction to Coroutines @ KotlinConf 2017

Coroutines: aftersuspend 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

Page 38: Introduction to Coroutines @ KotlinConf 2017

• Regular loops

Bonus features

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

}

Page 39: Introduction to Coroutines @ KotlinConf 2017

• Regular exception handing

Bonus features

try { createPost(token, item)

} catch (e: BadTokenException) { …

}

Page 40: Introduction to Coroutines @ KotlinConf 2017

• 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

Page 41: Introduction to Coroutines @ KotlinConf 2017

Suspending functions

Page 42: Introduction to Coroutines @ KotlinConf 2017

Retrofit asyncinterface Service {

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

Page 43: Introduction to Coroutines @ KotlinConf 2017

Retrofit asyncinterface Service {

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

future

Page 44: Introduction to Coroutines @ KotlinConf 2017

Retrofit asyncinterface Service {

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

suspend fun createPost(token: Token, item: Item): Post =serviceInstance.createPost(token, item).await()

Page 45: Introduction to Coroutines @ KotlinConf 2017

Retrofit asyncinterface Service {

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

suspend fun createPost(token: Token, item: Item): Post =serviceInstance.createPost(token, item).await()

natural signature

Page 46: Introduction to Coroutines @ KotlinConf 2017

Retrofit asyncinterface 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

Page 47: Introduction to Coroutines @ KotlinConf 2017

CompositionBeyond sequential

Page 48: Introduction to Coroutines @ KotlinConf 2017

val post = createPost(token, item)

Page 49: Introduction to Coroutines @ KotlinConf 2017

Higher-order functions

val post = retryIO {createPost(token, item)

}

Page 50: Introduction to Coroutines @ KotlinConf 2017

Higher-order functionsval post = retryIO { createPost(token, item) }

suspend fun <T> retryIO(block: suspend () -> T): T {var curDelay = 1000L // start with 1 secwhile (true) {

try {return block()

} catch (e: IOException) {e.printStackTrace() // log the error

}delay(curDelay)curDelay = (curDelay * 2).coerceAtMost(60000L)

}}

Page 51: Introduction to Coroutines @ KotlinConf 2017

Higher-order functionsval post = retryIO { createPost(token, item) }

suspend fun <T> retryIO(block: suspend () -> T): T {var curDelay = 1000L // start with 1 secwhile (true) {

try {return block()

} catch (e: IOException) {e.printStackTrace() // log the error

}delay(curDelay)curDelay = (curDelay * 2).coerceAtMost(60000L)

}}

Page 52: Introduction to Coroutines @ KotlinConf 2017

Higher-order functionsval post = retryIO { createPost(token, item) }

suspend fun <T> retryIO(block: suspend () -> T): T {var curDelay = 1000L // start with 1 secwhile (true) {

try {return block()

} catch (e: IOException) {e.printStackTrace() // log the error

}delay(curDelay)curDelay = (curDelay * 2).coerceAtMost(60000L)

}}

suspending lambda

Page 53: Introduction to Coroutines @ KotlinConf 2017

Higher-order functionsval post = retryIO { createPost(token, item) }

suspend fun <T> retryIO(block: suspend () -> T): T {var curDelay = 1000L // start with 1 secwhile (true) {

try {return block()

} catch (e: IOException) {e.printStackTrace() // log the error

}delay(curDelay)curDelay = (curDelay * 2).coerceAtMost(60000L)

}}

Everything like in blocking code

Page 54: Introduction to Coroutines @ KotlinConf 2017

Higher-order functionsval post = retryIO { createPost(token, item) }

suspend fun <T> retryIO(block: suspend () -> T): T {var curDelay = 1000L // start with 1 secwhile (true) {

try {return block()

} catch (e: IOException) {e.printStackTrace() // log the error

}delay(curDelay)curDelay = (curDelay * 2).coerceAtMost(60000L)

}}

Page 55: Introduction to Coroutines @ KotlinConf 2017

Coroutine builders

Page 56: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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)

}

Page 57: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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)

}

Page 58: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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

Page 59: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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

Page 60: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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

Page 61: Introduction to Coroutines @ KotlinConf 2017

Coroutines revisitedsuspend 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

Page 62: Introduction to Coroutines @ KotlinConf 2017

Launch

fun postItem(item: Item) {launch {

val token = requestToken()val post = createPost(token, item)processPost(post)

}}

coroutine builder

Page 63: Introduction to Coroutines @ KotlinConf 2017

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

Page 64: Introduction to Coroutines @ KotlinConf 2017

fun postItem(item: Item) {launch {

val token = requestToken()val post = createPost(token, item)processPost(post)

}}

Page 65: Introduction to Coroutines @ KotlinConf 2017

fun postItem(item: Item) {launch(UI) {

val token = requestToken()val post = createPost(token, item)processPost(post)

}}

UI ContextJust specify the context

Page 66: Introduction to Coroutines @ KotlinConf 2017

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

Page 67: Introduction to Coroutines @ KotlinConf 2017

Where’s the magic of launch?

Page 68: Introduction to Coroutines @ KotlinConf 2017

fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit

): Job { … }

A regular function

Page 69: Introduction to Coroutines @ KotlinConf 2017

fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit

): Job { … } suspending lambda

Page 70: Introduction to Coroutines @ KotlinConf 2017

fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit

): Job { … }

Page 71: Introduction to Coroutines @ KotlinConf 2017

async / await

Page 72: Introduction to Coroutines @ KotlinConf 2017

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

Page 73: Introduction to Coroutines @ KotlinConf 2017

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

Page 74: Introduction to Coroutines @ KotlinConf 2017

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

}

Classic-way

mark with asyncC#

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

Page 75: Introduction to Coroutines @ KotlinConf 2017

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

Page 76: Introduction to Coroutines @ KotlinConf 2017

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

Page 77: Introduction to Coroutines @ KotlinConf 2017

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

Page 78: Introduction to Coroutines @ KotlinConf 2017

Kotlin suspending functions are designed to imitate

sequential behavior by default

Concurrency is hardConcurrency has to be explicit

Page 79: Introduction to Coroutines @ KotlinConf 2017

Kotlin approach to asyncConcurrency where you need it

Page 80: Introduction to Coroutines @ KotlinConf 2017

Use-case for asyncasync Task<Image> loadImageAsync(String name) { … } C#

Page 81: Introduction to Coroutines @ KotlinConf 2017

Use-case for async

var promise1 = loadImageAsync(name1);var promise2 = loadImageAsync(name2);

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

Start multiple operations concurrently

C#

Page 82: Introduction to Coroutines @ KotlinConf 2017

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#

Page 83: Introduction to Coroutines @ KotlinConf 2017

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

Page 84: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun loadImageAsync(name: String): Deferred<Image> =

async { … }Kotlin

Page 85: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun loadImageAsync(name: String): Deferred<Image> =

async { … }Kotlin

A regular function

Page 86: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun loadImageAsync(name: String): Deferred<Image> =

async { … }

Kotlin’s future type

Kotlin

Page 87: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun loadImageAsync(name: String): Deferred<Image> =

async { … }

async coroutine builder

Kotlin

Page 88: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun loadImageAsync(name: String): Deferred<Image> =

async { … }

val deferred1 = loadImageAsync(name1)val deferred2 = loadImageAsync(name2)

Start multiple operations concurrently

Kotlin

Page 89: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun 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

Page 90: Introduction to Coroutines @ KotlinConf 2017

Kotlin async functionfun 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

Page 91: Introduction to Coroutines @ KotlinConf 2017

Using async function when needed

suspend fun loadImage(name: String): Image { … }

Is defined as suspending function, not async

Page 92: Introduction to Coroutines @ KotlinConf 2017

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

}

Page 93: Introduction to Coroutines @ KotlinConf 2017

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

}

Page 94: Introduction to Coroutines @ KotlinConf 2017

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

}

Page 95: Introduction to Coroutines @ KotlinConf 2017

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

}

Page 96: Introduction to Coroutines @ KotlinConf 2017

Kotlin approach to async

requestToken() VALID –> produces Token

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

sequential behavior

concurrent behavior

Kotlin

Kotlin

default

Page 97: Introduction to Coroutines @ KotlinConf 2017

Coroutines

Page 98: Introduction to Coroutines @ KotlinConf 2017

What are coroutines conceptually?

Page 99: Introduction to Coroutines @ KotlinConf 2017

What are coroutines conceptually?

Coroutines are like very light-weight threads

Page 100: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {

launch {delay(1000L)print(".")

}}jobs.forEach { it.join() }

}

Example

Page 101: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {

launch {delay(1000L)print(".")

}}jobs.forEach { it.join() }

}

ExampleThis coroutine builder runs coroutine

in the context of invoker thread

Page 102: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {

launch {delay(1000L)print(".")

}}jobs.forEach { it.join() }

}

Example

Page 103: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {

launch {delay(1000L)print(".")

}}jobs.forEach { it.join() }

}

Example

Page 104: Introduction to Coroutines @ KotlinConf 2017

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

Page 105: Introduction to Coroutines @ KotlinConf 2017

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

Page 106: Introduction to Coroutines @ KotlinConf 2017

Demo

Page 107: Introduction to Coroutines @ KotlinConf 2017

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

Page 108: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {

launch {delay(1000L)print(".")

}}jobs.forEach { it.join() }

}

Example

Page 109: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) {val jobs = List(100_000) {

thread {Thread.sleep(1000L)print(".")

}}jobs.forEach { it.join() }

}

Example

Page 110: Introduction to Coroutines @ KotlinConf 2017

Demo

Page 111: Introduction to Coroutines @ KotlinConf 2017

fun main(args: Array<String>) {val jobs = List(100_000) {

thread {Thread.sleep(1000L)print(".")

}}jobs.forEach { it.join() }

}

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

Page 112: Introduction to Coroutines @ KotlinConf 2017

Java interop

Page 113: Introduction to Coroutines @ KotlinConf 2017

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

Page 114: Introduction to Coroutines @ KotlinConf 2017

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

CompletableFuture<Image> loadAndCombineAsync(String name1, String name2)

Imagine implementing it in Java…

Java

Page 115: Introduction to Coroutines @ KotlinConf 2017

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

Page 116: Introduction to Coroutines @ KotlinConf 2017

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

Page 117: Introduction to Coroutines @ KotlinConf 2017

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

Page 118: Introduction to Coroutines @ KotlinConf 2017

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

Page 119: Introduction to Coroutines @ KotlinConf 2017

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 builderKotlin

Java

Page 120: Introduction to Coroutines @ KotlinConf 2017

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

Page 121: Introduction to Coroutines @ KotlinConf 2017

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

Page 122: Introduction to Coroutines @ KotlinConf 2017

Beyond asynchronous code

Page 123: Introduction to Coroutines @ KotlinConf 2017

Fibonacci sequenceval fibonacci = buildSequence {

var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

Page 124: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

A coroutine builder with restricted suspension

Page 125: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

A suspending function

Page 126: Introduction to Coroutines @ KotlinConf 2017

The same building blocksfun <T> buildSequence(

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

Page 127: Introduction to Coroutines @ KotlinConf 2017

fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit

): Sequence<T> { … }

Result is a synchronous sequence

Page 128: Introduction to Coroutines @ KotlinConf 2017

fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit

): Sequence<T> { … }Suspending lambda with receiver

Page 129: Introduction to Coroutines @ KotlinConf 2017

fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit

): Sequence<T> { … }

@RestrictsSuspensionabstract class SequenceBuilder<in T> {

abstract suspend fun yield(value: T)}

Coroutine is restricted only to suspending functions defined here

Page 130: Introduction to Coroutines @ KotlinConf 2017

Synchronousval fibonacci = buildSequence {

var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()

Page 131: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next())

Page 132: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next())

Page 133: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1

Page 134: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1println(iter.next())

Page 135: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1println(iter.next())

Page 136: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1println(iter.next()) // 1

Page 137: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1println(iter.next()) // 1println(iter.next()) // 2

Page 138: Introduction to Coroutines @ KotlinConf 2017

val fibonacci = buildSequence {var cur = 1var next = 1while (true) {

yield(cur) val tmp = cur + nextcur = nextnext = tmp

}}

val iter = fibonacci.iterator()println(iter.next()) // 1println(iter.next()) // 1println(iter.next()) // 2

Synchronous with invoker

Page 139: Introduction to Coroutines @ KotlinConf 2017

Library vs Language

Page 140: Introduction to Coroutines @ KotlinConf 2017

Classic async

async/awaitgenerate/yield Keywords

Page 141: Introduction to Coroutines @ KotlinConf 2017

Kotlin coroutines

suspend Modifier

Page 142: Introduction to Coroutines @ KotlinConf 2017

Kotlin coroutines

Standardlibrary

Page 143: Introduction to Coroutines @ KotlinConf 2017

Kotlin coroutines

Standardlibrary

kotlinx-coroutines

launch, async, runBlocking, future, delay,

Job, Deferred, etc

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

Page 144: Introduction to Coroutines @ KotlinConf 2017

Experimental statusCoroutines are here to stay

Backwards compatible inside 1.1 & 1.2

To be finalized in the future

Page 145: Introduction to Coroutines @ KotlinConf 2017

#kotlinconf17

relizarovelizarov at JetBrains

Roman Elizarov

Thank you

Any questions?