Slide 1

Slide 1 text

Kotlin Coroutines Asynchronous programming with ease

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Asynchronous Programming with Callbacks

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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 7

Slide 7 text

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 8

Slide 8 text

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 9

Slide 9 text

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 10

Slide 10 text

Futures/Promises make it nicer

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Futures: after fun requestTokenAsync(): Promise { // makes request for a token // returns promise for a future result immediately } 1 future

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Futures: after fun requestTokenAsync(): Promise { … } fun createPostAsync(token: Token, item: Item): Promise … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } } Callbacks are still here

Slide 18

Slide 18 text

Futures: after fun requestTokenAsync(): Promise { … } fun createPostAsync(token: Token, item: Item): Promise … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } } Composable & propagates exceptions No nesting indentation Callbacks are still here

Slide 19

Slide 19 text

Futures: after fun requestTokenAsync(): Promise { … } fun createPostAsync(token: Token, item: Item): Promise … fun processPost(post: Post) { … } fun postItem(item: Item) { requestTokenAsync() .thenCompose { token -> createPostAsync(token, item) } .thenAccept { post -> processPost(post) } } But all those operators…

Slide 20

Slide 20 text

Kotlin coroutines to the rescue

Slide 21

Slide 21 text

Coroutines: before fun requestTokenAsync(): Promise { // makes request for a token // returns promise for a future result immediately } 1

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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 natural signature

Slide 25

Slide 25 text

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 26

Slide 26 text

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 27

Slide 27 text

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 28

Slide 28 text

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 29

Slide 29 text

• Regular loops Bonus features for ((token, item) in list) { createPost(token, item) }

Slide 30

Slide 30 text

• Regular exception handing Bonus features try { createPost(token, item) } catch (e: BadTokenException) { … }

Slide 31

Slide 31 text

• Regular higher-order functions Bonus features file.readLines().forEach { line -> createPost(token, line.toItem()) }

Slide 32

Slide 32 text

• Custom higher-order functions Bonus features val post = retryIO { createPost(token, item) } Everything like in regular code

Slide 33

Slide 33 text

How does it work? A quick peek behind the scenes

Slide 34

Slide 34 text

Kotlin suspending functions callback Kotlin Java/JVM suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation cont) { … }

Slide 35

Slide 35 text

Kotlin suspending functions callback Kotlin Java/JVM Continuation is a generic callback interface suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation cont) { … } interface Continuation { val context: CoroutineContext fun resumeWith(result: Result) }

Slide 36

Slide 36 text

Kotlin suspending functions callback Kotlin Java/JVM suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation cont) { … } interface Continuation { val context: CoroutineContext fun resumeWith(result: Result) }

Slide 37

Slide 37 text

Kotlin suspending functions callback Kotlin Java/JVM suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation cont) { … } interface Continuation { val context: CoroutineContext fun resumeWith(result: Result) }

Slide 38

Slide 38 text

Integration Zoo of futures on JVM

Slide 39

Slide 39 text

interface Service { fun createPost(token: Token, item: Item): Call } Retrofit async

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

interface Service { fun createPost(token: Token, item: Item): Call } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await() Suspending extension function from integration library

Slide 42

Slide 42 text

suspend fun Call.await(): T { … }

Slide 43

Slide 43 text

Callbacks everywhere suspend fun Call.await(): T { enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { // todo } override fun onFailure(call: Call, t: Throwable) { // todo } }) }

Slide 44

Slide 44 text

suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) }

Slide 45

Slide 45 text

suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

Slide 46

Slide 46 text

suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

Slide 47

Slide 47 text

suspend fun suspendCoroutine(block: (Continuation) -> Unit): T Regular function Inspired by call/cc from Scheme

Slide 48

Slide 48 text

suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) }

Slide 49

Slide 49 text

Install callback suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) }

Slide 50

Slide 50 text

Install callback suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) }

Slide 51

Slide 51 text

Analyze response suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) }

Slide 52

Slide 52 text

Analyze response suspend fun Call.await(): T = suspendCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) cont.resume(response.body()!!) else cont.resumeWithException(ErrorResponse(response)) } override fun onFailure(call: Call, t: Throwable) { cont.resumeWithException(t) } }) } That’s all

Slide 53

Slide 53 text

Coroutine builders

Slide 54

Slide 54 text

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 55

Slide 55 text

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 56

Slide 56 text

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 57

Slide 57 text

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 58

Slide 58 text

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 59

Slide 59 text

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 60

Slide 60 text

Launch fun postItem(item: Item) { GlobalScope.launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } coroutine builder

Slide 61

Slide 61 text

fun postItem(item: Item) { GlobalScope.launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } Fire and forget!

Slide 62

Slide 62 text

fun postItem(item: Item) { GlobalScope.launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } Fire and forget! We launch for its side effects

Slide 63

Slide 63 text

fun postItem(item: Item) { GlobalScope.launch { val token = requestToken() val post = createPost(token, item) processPost(post) } } Returns immediately, coroutine works in background thread pool

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

fun postItem(item: Item) { GlobalScope.launch(Dispatchers.Main) { val token = requestToken() val post = createPost(token, item) processPost(post) } } Dispatcher Just specify the dispatcher

Slide 66

Slide 66 text

fun postItem(item: Item) { GlobalScope.launch(Dispatchers.Main) { val token = requestToken() val post = createPost(token, item) processPost(post) } } Dispatcher And it gets executed on the Main thread

Slide 67

Slide 67 text

Where’s the magic of launch?

Slide 68

Slide 68 text

fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, block: suspend () -> Unit ): Job { … } A regular function

Slide 69

Slide 69 text

fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, block: suspend () -> Unit ): Job { … } suspending lambda

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

async / await The classic approach

Slide 72

Slide 72 text

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 text

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 requestToken() { … } async Task createPost(Token token, Item item) { … } void processPost(Post post) { … }

Slide 74

Slide 74 text

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 requestToken() { … } async Task createPost(Token token, Item item) { … } void processPost(Post post) { … }

Slide 75

Slide 75 text

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 requestToken() { … } async Task createPost(Token token, Item item) { … } void processPost(Post post) { … }

Slide 76

Slide 76 text

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 requestToken() { … } async Task createPost(Token token, Item item) { … } void processPost(Post post) { … }

Slide 77

Slide 77 text

Why no await keyword in Kotlin? The problem with async requestToken() VALID –> produces Task await requestToken() VALID –> produces Token concurrent behavior sequential behavior C# C# default

Slide 78

Slide 78 text

Kotlin suspending functions are designed to imitate sequential behavior by default Concurrency is hard Concurrency has to be explicit

Slide 79

Slide 79 text

Kotlin approach to async Concurrency where you need it

Slide 80

Slide 80 text

Use-case for async async Task loadImageAsync(String name) { … } C#

Slide 81

Slide 81 text

Use-case for async var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); async Task loadImageAsync(String name) { … } Start multiple operations concurrently C#

Slide 82

Slide 82 text

Use-case for async var promise1 = loadImageAsync(name1); var promise2 = loadImageAsync(name2); var image1 = await promise1; var image2 = await promise2; async Task loadImageAsync(String name) { … } and then wait for them C#

Slide 83

Slide 83 text

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 loadImageAsync(String name) { … }

Slide 84

Slide 84 text

Kotlin async function fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } Kotlin

Slide 85

Slide 85 text

Kotlin async function fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } Kotlin A regular function

Slide 86

Slide 86 text

Kotlin async function fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } Kotlin’s future type Kotlin

Slide 87

Slide 87 text

Kotlin async function fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } async coroutine builder Kotlin

Slide 88

Slide 88 text

fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) Start multiple operations concurrently Kotlin

Slide 89

Slide 89 text

fun loadImageAsync(name: String): Deferred = GlobalScope.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 text

fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() val result = combineImages(image1, image2) Kotlin But what if this crashes? Crash?

Slide 91

Slide 91 text

fun loadImageAsync(name: String): Deferred = GlobalScope.async { … } val deferred1 = loadImageAsync(name1) val deferred2 = loadImageAsync(name2) val image1 = deferred1.await() val image2 = deferred2.await() val result = combineImages(image1, image2) Kotlin Crash? This one leaks But what if this crashes?

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

Idiomatic: Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image = coroutineScope { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } combineImages(deferred1.await(), deferred2.await()) } Structured concurrency

Slide 94

Slide 94 text

Idiomatic: Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image = coroutineScope { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } combineImages(deferred1.await(), deferred2.await()) }

Slide 95

Slide 95 text

Idiomatic: Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image = coroutineScope { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } combineImages(deferred1.await(), deferred2.await()) }

Slide 96

Slide 96 text

Idiomatic: Using async function when needed suspend fun loadImage(name: String): Image { … } suspend fun loadAndCombine(name1: String, name2: String): Image = coroutineScope { val deferred1 = async { loadImage(name1) } val deferred2 = async { loadImage(name2) } combineImages(deferred1.await(), deferred2.await()) }

Slide 97

Slide 97 text

Kotlin approach to async requestToken() VALID –> produces Token async { requestToken() } VALID –> produces Deferred sequential behavior concurrent behavior Kotlin Kotlin default

Slide 98

Slide 98 text

Structured concurrency

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

Kotlin suspending functions may use concurrency internally, but don’t leak it Concurrency needs to be structured

Slide 104

Slide 104 text

Parallelism is optional

Slide 105

Slide 105 text

Beyond asynchronous code Kotlin’s approach to generate/yield – synchronous coroutines

Slide 106

Slide 106 text

Fibonacci sequence val fibonacci: Sequence = …

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Fibonacci sequence val fibonacci = sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } println(fibonacci.take(10).toList())

Slide 109

Slide 109 text

Fibonacci sequence val fibonacci = sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } println(fibonacci.take(10).toList()) >> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Slide 110

Slide 110 text

Fibonacci sequence val fibonacci = sequence { 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 111

Slide 111 text

Fibonacci sequence val fibonacci = sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } A suspending function in the scope of buildSequence

Slide 112

Slide 112 text

Fibonacci sequence val fibonacci = sequence { var cur = 1 var next = 1 while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } } println(fibonacci.take(10).toList()) Synchronous

Slide 113

Slide 113 text

Library vs Language Keeping the core language small

Slide 114

Slide 114 text

Classic async async/await generate/yield Keywords

Slide 115

Slide 115 text

Kotlin coroutines suspend Modifier

Slide 116

Slide 116 text

Kotlin coroutines Standard library

Slide 117

Slide 117 text

Kotlin coroutines Standard library kotlinx-coroutines launch, async, runBlocking, Job, Deferred, etc http://github.com/kotlin/kotlinx.coroutines

Slide 118

Slide 118 text

No content

Slide 119

Slide 119 text

Thank you Any questions? elizarov @ Roman Elizarov relizarov