Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Asynchronous Data Streams with Kotlin Flow

Asynchronous Data Streams with Kotlin Flow

Kotlin Flow is a declarative mechanism for working with asynchronous data streams that builds on top of Kotlin coroutines and structured concurrency. Kotlin Flows are doing the same revolution in simplicity for data streams that suspending functions did for data values.

In this talk we will discuss the challenges of working with asynchronous streams and how Kotlin Flows solve them. We will study the basics of Kotlin Flow design, see their typical usage patterns, peek behind the scenes and into some of the implementation details, checkout flow performance and see how they enable writing safe, reliable, and leak-free systems. We will also discuss how they relate to and incorporate ideas from reactive extensions and reactive streams, how they are similar and different, and how they can be used together.

F9c354e780ce562daea0e21b99bfdc0d?s=128

Roman Elizarov

December 06, 2019
Tweet

More Decks by Roman Elizarov

Other Decks in Programming

Transcript

  1. Copenhagen Denmark ASYNCHRONOUS DATA STREAMS WITH KOTLIN FLOW ROMAN ELIZAROV

    @relizarov Copenhagen Denmark
  2. RECAP ON KOTLIN COROUTINES Kotlin Coroutines FTW

  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) } }
  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
  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) }
  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) }
  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) }
  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) }
  9. suspend fun foo(): Response One response suspend fun foo(): List<Response>

    Many responses
  10. suspend fun foo(): List<Response>

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

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

    }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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
  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
  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
  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
  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) }
  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) }
  25. Channel receive() send()

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

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

  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  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) }
  45. Channel is hot

  46. fun main() = runBlocking { val channel = foo() for

    (x in channel) println(x) } Channel is hot
  47. Channel is hot fun main() = runBlocking { val channel

    = foo() // for (x in channel) println(x) }
  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) }
  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) }
  50. fun CoroutineScope.foo(): ReceiveChannel<Response>

  51. KOTLIN FLOW Image: Markus Trienke, Sunset over dri6 ice

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

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

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

  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  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")) }
  74. fun main() = runBlocking { val flow = foo() flow.collect

    { x -> println(x) } } Flow is cold ❄
  75. fun main() = runBlocking { val flow = foo() //

    flow.collect { x -> println(x) } } Flow is cold ❄
  76. Flow is declara6ve

  77. Flow is declarative fun foo(): Flow<Response> = flow { emit(compute("A"))

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

  79. fun strings(): Flow<String> = flow { … } fun foo():

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

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

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

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

    compute(name) } Operators
  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) }
  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
  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
  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
  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
  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
  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
  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
  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
  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
  94. Flow is reac6ve RxJava Project Reactor Kotlin Flow Reactive Streams

    Specification
  95. Publisher<T> org.reactivestreams Publisher<T>

  96. fun <T : Any> Publisher<T>.asFlow(): Flow<T> org.reactivestreams Publisher<T> kotlinx.coroutines.flow Flow<T>

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

    Publisher<T>.asFlow(): Flow<T> org.reactivestreams Publisher<T> kotlinx.coroutines.flow Flow<T>
  98. WHY FLOW? What’s the difference?

  99. A A’ mapper fun map(mapper: (T) -> R): Flowable<R> fun

    flatMapSingle(mapper: (T) -> SingleSource<R>): Flowable<R> Synchronous Asynchronous Flowable<T>
  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>
  101. A A’ Flow<T> fun map(transform: suspend (T) -> R): Flow<R>

    transform
  102. A A’ transform Flow<T> fun map(transform: suspend (T) -> R):

    Flow<R>
  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
  104. Operator avoidance startWith(value) onStart { emit(value) } delaySubscription(time) onStart {

    delay(time) } startWith(flow) onStart { emitAll(flow) } delayEach(time) onEach { delay(time) } onErrorReturn(value) catch { emit(value) } onErrorResume(flow) catch { emitAll(flow) } generate(…) flow { … } Composable
  105. Reactive + = ❤

  106. FLOW UNDER THE HOOD Image: Flow by Grant Tarrant

  107. interface Flow<out T> { suspend fun collect(collector: FlowCollector<T>) } interface

    FlowCollector<in T> { suspend fun emit(value: T) }
  108. flow.collect { value -> println(value) } collect 1

  109. flow.collect { value -> println(value) } val flow = flow

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

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

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

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

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

    { emit("A") } collect emit λ println 1 2 3 λ
  115. flow.collect { value -> println(value) } val flow = flow

    { emit("A") } collect λ emit λ println 1 2 3 ^ 4 ^ 5
  116. flow.collect { value -> println(value) } val flow = flow

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

    { emit("A") delay(100) emit("B") } collect λ emit λ println emit println Asynchronous emi@er
  118. flow.collect { value -> delay(100) println(value) } val flow =

    flow { emit("A") delay(100) emit("B") } collect λ emit λ println emit println Backpressure
  119. Simple design

  120. Simple design ⇒ performance

  121. Kotlin Flow Plays Scrabble • Benchmark originally developed by José

    Paumard • Implemented for RxJava by David Karnok
  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
  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
  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
  125. FLOW IS ASYNCHRONOUS

  126. FLOW IS ASYNCHRONOUS YET SEQUENTIAL

  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
  128. GOING CONCURRENT WITH A FLOW Image: Channels by Tom Doel

  129. flow.buffer().collect { … }

  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
  131. flow.buffer().collect { … } main() emit emit emit A B

    C collect A B C Separate corouDne
  132. flow.buffer().collect { … } main() send send send A B

    C collect A B C ❌ Channel Declarative & safe
  133. FLOW EXECUTION CONTEXT Image: Context by Bart Everson

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

    compute(name) }
  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?
  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
  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
  138. FLOW IN REACTIVE UI

  139. fun events(): Flow<Event>

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

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

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

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

    } } “Subscribe” to events
  144. fun events(): Flow<Event> events() .onEach { event -> updateUI(event) }

    .launchIn(scope) “Subscribe” to events
  145. fun events(): Flow<Event> events() .onEach { event -> updateUI(event) }

    .launchIn(scope) “Subscribe” to events
  146. MANAGING LIFETIME

  147. Observable • ………. • ….. • ……..

  148. Observable • ………. • ….. • …….. subscribe observable.subscribe {

    event -> updateUI(event) }
  149. Observable • ………. • ….. • …….. subscribe SubscripIon observable.subscribe

    { event -> updateUI(event) }
  150. Observable • ………. • ….. • …….. subscribe SubscripIon val

    composite = CompositeDisposable() composite.add(observable.subscribe { event -> updateUI(event) })
  151. Observable • ………. • ….. • …….. subscribe SubscripIon val

    composite = CompositeDisposable() composite.add(observable.subscribe { event -> updateUI(event) }) composite.clear()
  152. Flow • ………. • ….. • …….. launch Job CorouIneScope

    Structured Concurrency
  153. Flow • ………. • ….. • …….. launch Job events()

    .onEach { event -> updateUI(event) } .launchIn(scope)
  154. Flow • ………. • ….. • …….. launch Job val

    scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope)
  155. Flow • ………. • ….. • …….. launch Job val

    scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope) scope.cancel()
  156. Flow • ………. • ….. • …….. launch Job val

    scope = MainScope() events() .onEach { event -> updateUI(event) } .launchIn(scope) scope.cancel()
  157. LiveData • ………. • ….. • …….. observe data.observe(livecycleOwner) {

    event -> updateUI(event) }
  158. STATUS & ROADMAP What’s next?

  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
  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
  161. #KotlinConf THANK YOU AND REMEMBER TO VOTE Roman Elizarov @relizarov

    #KotlinConf