ASYNCHRONOUS DATA STREAMS WITH KOTLIN FLOW ROMAN ELIZAROV - - PowerPoint PPT Presentation

asynchronous data streams with kotlin flow roman elizarov
SMART_READER_LITE
LIVE PREVIEW

ASYNCHRONOUS DATA STREAMS WITH KOTLIN FLOW ROMAN ELIZAROV - - PowerPoint PPT Presentation

ASYNCHRONOUS DATA STREAMS WITH KOTLIN FLOW ROMAN ELIZAROV @relizarov Copenhagen Copenhagen Denmark Denmark RECAP ON KOTLIN COROUTINES Kotlin Coroutines FTW Callback hell before fun requestTokenAsync(): Promise<Token> { } fun


slide-1
SLIDE 1

Copenhagen Denmark

ASYNCHRONOUS DATA STREAMS WITH KOTLIN FLOW ROMAN ELIZAROV

@relizarov

Copenhagen Denmark

slide-2
SLIDE 2

RECAP ON KOTLIN COROUTINES

Kotlin Coroutines FTW

slide-3
SLIDE 3

Callback hell before

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

slide-4
SLIDE 4

Direct style with Kotlin Corou6nes

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-5
SLIDE 5

Direct style with Kotlin Coroutines

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-6
SLIDE 6

Asynchronous yet sequen6al

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-7
SLIDE 7

Asynchronous yet sequential

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-8
SLIDE 8

Asynchronous yet sequential

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-9
SLIDE 9

suspend fun foo(): Response One response suspend fun foo(): List<Response> Many responses

slide-10
SLIDE 10

suspend fun foo(): List<Response>

slide-11
SLIDE 11

suspend fun foo(): List<Response> = buildList { … }

slide-12
SLIDE 12

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) }

slide-13
SLIDE 13

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-14
SLIDE 14

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-15
SLIDE 15

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-16
SLIDE 16

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-17
SLIDE 17

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-18
SLIDE 18

main() foo()

A

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-19
SLIDE 19

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

B A

slide-20
SLIDE 20

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

C B A

slide-21
SLIDE 21

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) } List<Response>

C B A

slide-22
SLIDE 22

main() foo() suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) } List<Response>

C B A

slide-23
SLIDE 23

main() foo() List<Response>

A B C B C A

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-24
SLIDE 24

main() foo() List<Response>

A B C B C A

suspend fun foo(): List<Response> = buildList { add(compute("A")) add(compute("B")) add(compute("C")) } fun main() = runBlocking { val list = foo() for (x in list) println(x) }

slide-25
SLIDE 25

Channel

receive() send()

slide-26
SLIDE 26

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { … }

slide-27
SLIDE 27

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) }

slide-28
SLIDE 28

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-29
SLIDE 29

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-30
SLIDE 30

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-31
SLIDE 31

main() foo() fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-32
SLIDE 32

main() foo() fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-33
SLIDE 33

main() foo() Channel<R> fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-34
SLIDE 34

main() foo() Channel<R> fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-35
SLIDE 35

main() foo() Channel<R> fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-36
SLIDE 36

main() foo() send Channel<R>

A

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-37
SLIDE 37

main() foo() send Channel<R>

A A

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-38
SLIDE 38

main() foo() send send Channel<R>

A A B

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-39
SLIDE 39

main() foo() send send Channel<R>

A B A B

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-40
SLIDE 40

main() foo() send send send Channel<R>

A B A B C

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-41
SLIDE 41

main() foo() send send send Channel<R>

A B C A B C

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-42
SLIDE 42

main() foo() send send send Channel<R>

A B C A B C

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-43
SLIDE 43

main() foo() send send send Channel<R>

A B C A B C

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-44
SLIDE 44

main() foo() send send send Channel<R>

A B C A B C ❌

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

slide-45
SLIDE 45

Channel is hot 🔦

slide-46
SLIDE 46

fun main() = runBlocking { val channel = foo() for (x in channel) println(x) }

Channel is hot 🔦

slide-47
SLIDE 47

Channel is hot 🔦

fun main() = runBlocking { val channel = foo() // for (x in channel) println(x) }

slide-48
SLIDE 48

main() foo() Channel fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() // for (x in channel) println(x) }

slide-49
SLIDE 49

main() foo() send Channel

🔦

A

fun CoroutineScope.foo(): ReceiveChannel<Response> = produce { send(compute("A")) send(compute("B")) send(compute("C")) } fun main() = runBlocking { val channel = foo() // for (x in channel) println(x) }

slide-50
SLIDE 50

fun CoroutineScope.foo(): ReceiveChannel<Response>

🔦

slide-51
SLIDE 51

KOTLIN FLOW

Image: Markus Trienke, Sunset over dri6 ice

slide-52
SLIDE 52

fun foo(): Flow<Response> = flow { … }

slide-53
SLIDE 53

fun foo(): Flow<Response> = flow { … }

slide-54
SLIDE 54

fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-55
SLIDE 55

fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-56
SLIDE 56

fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-57
SLIDE 57

fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-58
SLIDE 58

main() foo() fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-59
SLIDE 59

main() foo() fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-60
SLIDE 60

main() foo() Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-61
SLIDE 61

main() foo() Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-62
SLIDE 62

main() foo() collect Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-63
SLIDE 63

main() foo() emit collect

A

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-64
SLIDE 64

main() foo() emit

A

collect

A

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-65
SLIDE 65

main() foo() emit

A

collect

A

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-66
SLIDE 66

main() foo() emit emit

A

collect

A B

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-67
SLIDE 67

main() foo() emit emit

A B

collect

A B

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-68
SLIDE 68

main() foo() emit emit

A B

collect

A B

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-69
SLIDE 69

main() foo() emit emit emit

A B

collect

A B C

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-70
SLIDE 70

main() foo() emit emit emit

A B C

collect

A B C

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-71
SLIDE 71

main() foo() emit emit emit

A B C

collect

A B C

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-72
SLIDE 72

main() foo() emit emit emit

A B C

collect

A B C ❌

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-73
SLIDE 73

main() foo() emit emit emit

A B C

collect

A B C ❌

Flow<R> fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } } fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

slide-74
SLIDE 74

fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } }

Flow is cold ❄

slide-75
SLIDE 75

fun main() = runBlocking { val flow = foo() // flow.collect { x -> println(x) } }

Flow is cold ❄

😄

slide-76
SLIDE 76

Flow is declara6ve

slide-77
SLIDE 77

Flow is declarative

fun foo(): Flow<Response> = flow { emit(compute("A")) emit(compute("B")) emit(compute("C")) }

Declaration

slide-78
SLIDE 78

fun strings(): Flow<String> = flow { emit("A") emit("B") emit(”C") }

slide-79
SLIDE 79

fun strings(): Flow<String> = flow { … } fun foo(): Flow<Response> = strings().map { name -> compute(name) }

slide-80
SLIDE 80

fun strings(): Flow<String> = flow { … } fun foo(): Flow<Response> = strings().map { name -> compute(name) }

slide-81
SLIDE 81

fun strings(): Flow<String> = flow { … } fun foo(): Flow<Response> = strings().map { name -> compute(name) }

slide-82
SLIDE 82

fun strings(): Flow<String> = flow { … } fun foo(): Flow<Response> = strings().map { name -> compute(name) }

Operators

slide-83
SLIDE 83

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) }

Operators

slide-84
SLIDE 84

Flow vs List

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

slide-85
SLIDE 85

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

Defined – Declarative Runs – Impera:ve

suspend fun <T> Flow<T>.collect(…)

Runs the flow

Flow vs List

slide-86
SLIDE 86

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

Defined – Declara:ve Runs – Imperative

suspend fun <T> Flow<T>.toList(): List<T>

Runs the flow

Flow vs List

slide-87
SLIDE 87

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

Flow vs List Execu6on order

slide-88
SLIDE 88

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A

Flow vs List

slide-89
SLIDE 89

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A

map

Flow vs List

slide-90
SLIDE 90

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A B’ C’ A’

map

Flow vs List

slide-91
SLIDE 91

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A B’ C’ A’

map

A A’

Flow vs List

slide-92
SLIDE 92

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A B’ C’ A’

map

A B’ A’ B

Flow vs List

slide-93
SLIDE 93

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } suspend fun foo(): List<Response> = listOf("A", "B", "C").map { name -> compute(name) }

B C A B’ C’ A’

map

A B’ C’ A’ B C

Flow vs List

React on emi2ed values

slide-94
SLIDE 94

Flow is reac6ve 🚁

RxJava Project Reactor Kotlin Flow Reactive Streams Specification

slide-95
SLIDE 95

Publisher<T>

  • rg.reactivestreams

Publisher<T>

slide-96
SLIDE 96

fun <T : Any> Publisher<T>.asFlow(): Flow<T>

  • rg.reactivestreams

Publisher<T> kotlinx.coroutines.flow Flow<T>

slide-97
SLIDE 97

fun <T : Any> Flow<T>.asPublisher(): Publisher<T> fun <T : Any> Publisher<T>.asFlow(): Flow<T>

  • rg.reactivestreams

Publisher<T> kotlinx.coroutines.flow Flow<T>

slide-98
SLIDE 98

WHY FLOW?

What’s the difference?

slide-99
SLIDE 99

A A’

mapper fun map(mapper: (T) -> R): Flowable<R> fun flatMapSingle(mapper: (T) -> SingleSource<R>): Flowable<R>

Synchronous Asynchronous

Flowable<T>

slide-100
SLIDE 100

A A’

mapper fun map(mapper: (T) -> R): Flowable<R> fun flatMapSingle(mapper: (T) -> SingleSource<R>): Flowable<R> fun filter(predicate: (T) -> Boolean): Flowable<T>

A A

predicate

🤰

Synchronous Asynchronous Synchronous Asynchronous

Flowable<T>

slide-101
SLIDE 101

A A’

Flow<T> fun map(transform: suspend (T) -> R): Flow<R> transform

slide-102
SLIDE 102

A A’

transform Flow<T> fun map(transform: suspend (T) -> R): Flow<R>

slide-103
SLIDE 103

A A’

transform fun map(transform: suspend (T) -> R): Flow<R> Flow<T> fun filter(predicate: suspend (T) -> Boolean): Flow<T>

A

predicate

A

slide-104
SLIDE 104

Operator avoidance

startWith(value)

  • nStart { emit(value) }

delaySubscription(time)

  • nStart { delay(time) }

startWith(flow)

  • nStart { emitAll(flow) }

delayEach(time)

  • nEach { delay(time) }
  • nErrorReturn(value)

catch { emit(value) }

  • nErrorResume(flow)

catch { emitAll(flow) }

generate(…)

flow { … }

Composable

slide-105
SLIDE 105

Reactive + = ❤

slide-106
SLIDE 106

FLOW UNDER THE HOOD

Image: Flow by Grant Tarrant

slide-107
SLIDE 107

interface Flow<out T> { suspend fun collect(collector: FlowCollector<T>) } interface FlowCollector<in T> { suspend fun emit(value: T) }

slide-108
SLIDE 108

flow.collect { value -> println(value) } collect

1

slide-109
SLIDE 109

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect

1

slide-110
SLIDE 110

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect

1

slide-111
SLIDE 111

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect

1

λ

slide-112
SLIDE 112

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect

1

λ

slide-113
SLIDE 113

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect emit

λ

1 2

λ

slide-114
SLIDE 114

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect emit

λ

println

1 2 3

λ

slide-115
SLIDE 115

flow.collect { value -> println(value) } val flow = flow { emit("A") } collect

λ

emit

λ

println

1 2 3

^

4

^

5

slide-116
SLIDE 116

flow.collect { value -> println(value) } val flow = flow { emit("A") emit("B") } collect

λ

emit

λ

println emit println

slide-117
SLIDE 117

flow.collect { value -> println(value) } val flow = flow { emit("A") delay(100) emit("B") } collect

λ

emit

λ

println emit println

Asynchronous emi@er

slide-118
SLIDE 118

flow.collect { value -> delay(100) println(value) } val flow = flow { emit("A") delay(100) emit("B") } collect

λ

emit

λ

println emit println

Backpressure

slide-119
SLIDE 119

Simple design

slide-120
SLIDE 120

Simple design ⇒ performance

slide-121
SLIDE 121

Kotlin Flow Plays Scrabble

  • Benchmark originally developed by José Paumard
  • Implemented for RxJava by David Karnok
slide-122
SLIDE 122

Kotlin Flow Plays Scrabble

  • Benchmark originally developed by José Paumard
  • Implemented for RxJava by David Karnok

SequencePlaysScrabble 9.824 ± 0.190 ms/op

https://github.com/Kotlin/kotlinx.coroutines/tree/develop/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/README.md

slide-123
SLIDE 123

Kotlin Flow Plays Scrabble

  • Benchmark originally developed by José Paumard
  • Implemented for RxJava by David Karnok

SequencePlaysScrabble 9.824 ± 0.190 ms/op RxJava2PlaysScrabbleOpt 23.653 ± 0.379 ms/op

https://github.com/Kotlin/kotlinx.coroutines/tree/develop/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/README.md

slide-124
SLIDE 124

Kotlin Flow Plays Scrabble

  • Benchmark originally developed by José Paumard
  • Implemented for RxJava by David Karnok

SequencePlaysScrabble 9.824 ± 0.190 ms/op RxJava2PlaysScrabbleOpt 23.653 ± 0.379 ms/op FlowPlaysScrabbleOpt 13.958 ± 0.278 ms/op

https://github.com/Kotlin/kotlinx.coroutines/tree/develop/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/README.md

slide-125
SLIDE 125

FLOW IS ASYNCHRONOUS

slide-126
SLIDE 126

FLOW IS ASYNCHRONOUS YET SEQUENTIAL

slide-127
SLIDE 127

main() emit emit emit

A B C

collect

A B C ❌

100 ms 100 ms 100 ms 100 ms 100 ms 100 ms 100 ms 700 ms

Single corouDne

slide-128
SLIDE 128

GOING CONCURRENT WITH A FLOW

Image: Channels by Tom Doel

slide-129
SLIDE 129

flow.buffer().collect { … }

slide-130
SLIDE 130

flow.buffer().collect { … } main() emit emit emit

A B C

collect

A B C ❌

100 ms 100 ms 100 ms 100 ms 400 ms 100 ms 100 ms 100 ms

slide-131
SLIDE 131

flow.buffer().collect { … } main() emit emit emit

A B C

collect

A B C

Separate corouDne

slide-132
SLIDE 132

flow.buffer().collect { … } main() send send send

A B C

collect

A B C ❌

Channel

Declarative & safe

slide-133
SLIDE 133

FLOW EXECUTION CONTEXT

Image: Context by Bart Everson

slide-134
SLIDE 134

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) }

slide-135
SLIDE 135

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } }

Where does it execute?

slide-136
SLIDE 136

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } .flowOn(Dispatchers.Default) fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } }

Executes in background

slide-137
SLIDE 137

fun foo(): Flow<Response> = flowOf("A", "B", "C").map { name -> compute(name) } .flowOn(Dispatchers.Default)

Executes in collector’s context

fun main() = runBlocking { val flow = foo() flow.collect { x -> println(x) } }

Context preservation

slide-138
SLIDE 138

FLOW IN REACTIVE UI

slide-139
SLIDE 139

fun events(): Flow<Event>

slide-140
SLIDE 140

fun events(): Flow<Event> scope.launch { … }

slide-141
SLIDE 141

fun events(): Flow<Event> scope.launch { events().collect { event -> … } }

slide-142
SLIDE 142

fun events(): Flow<Event> scope.launch { events().collect { event -> updateUI(event) } }

slide-143
SLIDE 143

fun events(): Flow<Event> scope.launch { events().collect { event -> updateUI(event) } }

“Subscribe” to events

slide-144
SLIDE 144

fun events(): Flow<Event> events() .onEach { event -> updateUI(event) } .launchIn(scope)

“Subscribe” to events

slide-145
SLIDE 145

fun events(): Flow<Event> events() .onEach { event -> updateUI(event) } .launchIn(scope)

“Subscribe” to events

slide-146
SLIDE 146

MANAGING LIFETIME

slide-147
SLIDE 147

Observable

  • ……….
  • …..
  • ……..
slide-148
SLIDE 148

Observable

  • ……….
  • …..
  • ……..

subscribe

  • bservable.subscribe { event ->

updateUI(event) }

slide-149
SLIDE 149

Observable

  • ……….
  • …..
  • ……..

subscribe

SubscripIon

  • bservable.subscribe { event ->

updateUI(event) }

slide-150
SLIDE 150

Observable

  • ……….
  • …..
  • ……..

subscribe

SubscripIon

val composite = CompositeDisposable() composite.add(observable.subscribe { event -> updateUI(event) })

slide-151
SLIDE 151

Observable

  • ……….
  • …..
  • ……..

subscribe

SubscripIon

val composite = CompositeDisposable() composite.add(observable.subscribe { event -> updateUI(event) }) composite.clear()

slide-152
SLIDE 152

Flow

  • ……….
  • …..
  • ……..

launch

Job CorouIneScope Structured Concurrency

slide-153
SLIDE 153

Flow

  • ……….
  • …..
  • ……..

launch

Job

events() .onEach { event -> updateUI(event) } .launchIn(scope)

slide-154
SLIDE 154

Flow

  • ……….
  • …..
  • ……..

launch

Job

val scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope)

slide-155
SLIDE 155

Flow

  • ……….
  • …..
  • ……..

launch

Job

val scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope) scope.cancel()

slide-156
SLIDE 156

Flow

  • ……….
  • …..
  • ……..

launch

Job

val scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope) scope.cancel()

slide-157
SLIDE 157

LiveData

  • ……….
  • …..
  • ……..
  • bserve

data.observe(livecycleOwner) { event -> updateUI(event) }

slide-158
SLIDE 158

STATUS & ROADMAP

What’s next?

slide-159
SLIDE 159

Status & Roadmap

🚁 Flow is stable in kotlinx.corouInes version 1.3.0 🔯 Future improvements

ØOut-of-the box support for UI models (StateFlow / EventFlow) ØSharing / caching flows ØConcurrency / parallelism operators ØChunking / windowing operators

🦅 Want more? Give us your feedback h2ps://github.com/Kotlin/kotlinx.corouInes/issues

slide-160
SLIDE 160

Learn more

vKotlin Flow by example guide https://kotlinlang.org/docs/reference/coroutines/flow.html vAPI docs https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/ vStories in my blog https://medium.com/@elizarov

slide-161
SLIDE 161

#KotlinConf

THANK YOU AND REMEMBER TO VOTE

Roman Elizarov @relizarov #KotlinConf