Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow: Operator for action on successful completion #1693

Closed
xizzhu opened this issue Dec 9, 2019 · 3 comments
Closed

Flow: Operator for action on successful completion #1693

xizzhu opened this issue Dec 9, 2019 · 3 comments
Assignees

Comments

@xizzhu
Copy link

xizzhu commented Dec 9, 2019

According to the doc, the onCompletion() "operator is transparent to exceptions that occur in downstream flow and does not observe exceptions that are thrown to cancel the flow".

If I want to do something only when the flow is "successfully completed" (i.e. no exception, not cancelled), how can I do that? Thanks.

It can be reproduced with the following code:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun foo(): Flow<Int> = flow {
    var i = 0
    while (true) {
        emit(i++)
        delay(100)
    }
}

suspend fun main() {
    val job = GlobalScope.launch {
        foo().onEach { println("onEach: $it")}
            .onCompletion {
                println("onCompletion: $it")
            }
            .collect()
    }
    
    delay(500)
    println("after delay()")
    job.cancel()
    
    delay(100)
    println("after second delay()")
}
@circusmagnus
Copy link

circusmagnus commented Dec 9, 2019

But your flow will never successfully complete. It does not stop emitting unless canceled

fun foo(): Flow<Int> = flow {
    var i = 0
    while (true) {
        emit(i++)
        delay(100)
    }
}

If it had finite number of emissions, you could simply write your code after last call to emit():

fun foo(): Flow<Int> = flow {
   repeat(10) { 
      emit(whatever) // just emit values you wanted to emit
   }
   doSomethingAfterFlowEmittedAllValues() // will happen, after all emissions have been dealt with.
}

You can also easily write your own operator to do this further downstream:

Flow<T>.doAfterLast(action: () -> Unit): Flow<T> = flow {
   collect { value -> emit(value) } // just pass upstream values through
   action() // will happen, after all upstream values have been dealt with.
}

Or you can do this simply after final call to collect{}:

launch {
   someFlow.collect { ... } // collect flow as usual
   doSomething() // will happen after everything has been collected
}

In all cases, those actions will only be executed, if flow has been successfully collected (not canceled, not failed on exception).

@elizarov
Copy link
Contributor

elizarov commented Dec 9, 2019

@xizzhu In addition to the above comment (you can easily write your own operator for that), I would really like to know why you need that. What is your use-case? Why would you need an operator for successful (non-cancelled) completion?

@xizzhu
Copy link
Author

xizzhu commented Dec 9, 2019

Thanks @circusmagnus for the suggestion!

@elizarov I have a flow that downloads from a server, and emits the downloading progress. When the downloading finishes (not-cancelled), I want to show a toast to inform the user. Is the suggestion to put the "doAfterLastEmit" action directly after collect()?

@elizarov elizarov changed the title How to know if the flow was canceled in onCompletion? Flow: Operator for action on successful completion Dec 9, 2019
elizarov added a commit that referenced this issue Dec 24, 2019
Flow.onCompletion now reports all failures and cancellation in its cause just like invokeOnCompletion. A null cause is reported if and only if flow had completed successfully (no failure, no cancellation). Emission of additional elements after the end of the flow is only possible from inside of onCompletion block in case of successful completion.

Also fixed a bug where onCompletion implementation was trying to add exception to its own list of suppressed exceptions, which is not allowed.

Fixes #1693
@elizarov elizarov self-assigned this Dec 24, 2019
elizarov added a commit that referenced this issue Dec 27, 2019
Flow.onCompletion now reports all failures and cancellation in its cause just like invokeOnCompletion. A null cause is reported if and only if flow had completed successfully (no failure, no cancellation). Emission of additional elements after the end of the flow is only possible from inside of onCompletion block in case of successful completion.

Also fixed a bug where onCompletion implementation was trying to add exception to its own list of suppressed exceptions, which is not allowed.

Fixes #1693
elizarov added a commit that referenced this issue Apr 8, 2020
Flow.onCompletion now reports all failures and cancellation in its cause just like invokeOnCompletion. A null cause is reported if and only if flow had completed successfully (no failure, no cancellation). Emission of additional elements after the end of the flow is only possible from inside of onCompletion block in case of successful completion.

Also fixed a bug where onCompletion implementation was trying to add exception to its own list of suppressed exceptions, which is not allowed.

Fixes #1693
recheej pushed a commit to recheej/kotlinx.coroutines that referenced this issue Dec 28, 2020
…otlin#1732)

Flow.onCompletion now reports all failures and cancellation in its cause just like invokeOnCompletion. A null cause is reported if and only if flow had completed successfully (no failure, no cancellation). Emission of additional elements after the end of the flow is only possible from inside of onCompletion block in case of successful completion.

Also fixed a bug where onCompletion implementation was trying to add exception to its own list of suppressed exceptions, which is not allowed.

Fixes Kotlin#1693
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants