$30 off During Our Annual Pro Sale. View Details »

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.

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

    View Slide

  2. RECAP ON KOTLIN
    COROUTINES
    Kotlin Coroutines FTW

    View Slide

  3. Callback hell before
    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) }
    }

    View 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

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

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

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

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

    View Slide

  9. suspend fun foo(): Response One response
    suspend fun foo(): List Many responses

    View Slide

  10. suspend fun foo(): List

    View Slide

  11. suspend fun foo(): List = buildList {

    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. main()
    foo()
    suspend fun foo(): List = 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. Channel
    receive()
    send()

    View Slide

  26. fun CoroutineScope.foo(): ReceiveChannel = produce {

    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. Channel is hot

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  49. main()
    foo()
    send
    Channel

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

    View Slide

  50. fun CoroutineScope.foo(): ReceiveChannel

    View Slide

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

    View Slide

  52. fun foo(): Flow = flow {

    }

    View Slide

  53. fun foo(): Flow = flow {

    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. Flow is declara6ve

    View Slide

  77. Flow is declarative
    fun foo(): Flow = flow {
    emit(compute("A"))
    emit(compute("B"))
    emit(compute("C"))
    }
    Declaration

    View Slide

  78. fun strings(): Flow = flow {
    emit("A")
    emit("B")
    emit(”C")
    }

    View Slide

  79. fun strings(): Flow = flow {

    }
    fun foo(): Flow =
    strings().map { name ->
    compute(name)
    }

    View Slide

  80. fun strings(): Flow = flow {

    }
    fun foo(): Flow =
    strings().map { name ->
    compute(name)
    }

    View Slide

  81. fun strings(): Flow = flow {

    }
    fun foo(): Flow =
    strings().map { name ->
    compute(name)
    }

    View Slide

  82. fun strings(): Flow = flow {

    }
    fun foo(): Flow =
    strings().map { name ->
    compute(name)
    }
    Operators

    View Slide

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

    View Slide

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

    View Slide

  85. fun foo(): Flow =
    flowOf("A", "B", "C").map { name ->
    compute(name)
    }
    suspend fun foo(): List =
    listOf("A", "B", "C").map { name ->
    compute(name)
    }
    Defined – Declarative
    Runs – Impera:ve
    suspend fun Flow.collect(…) Runs the flow
    Flow vs List

    View Slide

  86. fun foo(): Flow =
    flowOf("A", "B", "C").map { name ->
    compute(name)
    }
    suspend fun foo(): List =
    listOf("A", "B", "C").map { name ->
    compute(name)
    }
    Defined – Declara:ve
    Runs – Imperative
    suspend fun Flow.toList(): List Runs the flow
    Flow vs List

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  93. fun foo(): Flow =
    flowOf("A", "B", "C").map { name ->
    compute(name)
    }
    suspend fun foo(): List =
    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

    View Slide

  94. Flow is reac6ve
    RxJava
    Project
    Reactor
    Kotlin Flow
    Reactive Streams Specification

    View Slide

  95. Publisher
    org.reactivestreams
    Publisher

    View Slide

  96. fun Publisher.asFlow(): Flow
    org.reactivestreams
    Publisher
    kotlinx.coroutines.flow
    Flow

    View Slide

  97. fun Flow.asPublisher(): Publisher
    fun Publisher.asFlow(): Flow
    org.reactivestreams
    Publisher
    kotlinx.coroutines.flow
    Flow

    View Slide

  98. WHY FLOW?
    What’s the difference?

    View Slide

  99. A
    A’
    mapper
    fun map(mapper: (T) -> R): Flowable
    fun flatMapSingle(mapper: (T) -> SingleSource): Flowable
    Synchronous
    Asynchronous
    Flowable

    View Slide

  100. A
    A’
    mapper
    fun map(mapper: (T) -> R): Flowable
    fun flatMapSingle(mapper: (T) -> SingleSource): Flowable
    fun filter(predicate: (T) -> Boolean): Flowable
    A
    A
    predicate

    Synchronous
    Asynchronous
    Synchronous
    Asynchronous
    Flowable

    View Slide

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

    View Slide

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

    View Slide

  103. A
    A’
    transform fun map(transform: suspend (T) -> R): Flow
    Flow
    fun filter(predicate: suspend (T) -> Boolean): Flow
    A
    predicate
    A

    View Slide

  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

    View Slide

  105. Reactive + = ❤

    View Slide

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

    View Slide

  107. interface Flow {
    suspend fun collect(collector: FlowCollector)
    }
    interface FlowCollector {
    suspend fun emit(value: T)
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  119. Simple design

    View Slide

  120. Simple design ⇒ performance

    View Slide

  121. Kotlin Flow Plays Scrabble
    • Benchmark originally developed by José Paumard
    • Implemented for RxJava by David Karnok

    View 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

    View 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

    View 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

    View Slide

  125. FLOW IS
    ASYNCHRONOUS

    View Slide

  126. FLOW IS
    ASYNCHRONOUS
    YET SEQUENTIAL

    View 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

    View Slide

  128. GOING
    CONCURRENT
    WITH A FLOW
    Image: Channels by Tom Doel

    View Slide

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

    View 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

    View Slide

  131. flow.buffer().collect { … }
    main()
    emit emit emit
    A B C
    collect
    A B C
    Separate corouDne

    View Slide

  132. flow.buffer().collect { … }
    main()
    send send send
    A B C
    collect
    A B C ❌
    Channel
    Declarative & safe

    View Slide

  133. FLOW
    EXECUTION CONTEXT
    Image: Context by Bart Everson

    View Slide

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

    View Slide

  135. fun foo(): Flow =
    flowOf("A", "B", "C").map { name ->
    compute(name)
    }
    fun main() = runBlocking {
    val flow = foo()
    flow.collect { x -> println(x) }
    }
    Where does it execute?

    View Slide

  136. fun foo(): Flow =
    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

    View Slide

  137. fun foo(): Flow =
    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

    View Slide

  138. FLOW IN REACTIVE UI

    View Slide

  139. fun events(): Flow

    View Slide

  140. fun events(): Flow
    scope.launch {

    }

    View Slide

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

    }
    }

    View Slide

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

    View Slide

  143. fun events(): Flow
    scope.launch {
    events().collect { event ->
    updateUI(event)
    }
    }
    “Subscribe” to events

    View Slide

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

    View Slide

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

    View Slide

  146. MANAGING LIFETIME

    View Slide

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

    View Slide

  148. Observable
    • ……….
    • …..
    • ……..
    subscribe
    observable.subscribe { event ->
    updateUI(event)
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  152. Flow
    • ……….
    • …..
    • ……..
    launch
    Job
    CorouIneScope
    Structured Concurrency

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  157. LiveData
    • ……….
    • …..
    • ……..
    observe
    data.observe(livecycleOwner) { event ->
    updateUI(event)
    }

    View Slide

  158. STATUS & ROADMAP
    What’s next?

    View 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

    View 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

    View Slide

  161. #KotlinConf
    THANK YOU
    AND
    REMEMBER
    TO VOTE
    Roman Elizarov @relizarov
    #KotlinConf

    View Slide