2

I am working on a program which has both a TUI (terminal user interface) as well as a GUI (graphical user interface).

The program has some functions which take quite a long time to complete (waiting on network requests and such). I know that I can use threads (or coroutines since I am using Kotlin) to have the long running task in the background, while the TUI or GUI does other things. However, I also have information that I want to convey while the task is running, so the user knows what's happening in the mean time (as opposed to just showing "Loading..." or a spinning circle icon).

My current guess is to use something like callbacks (or Channel in Kotlin) to convey information, like "I am now doing X", "I have completed X", "I failed to do X so I am now aborting", etc. I have given an example of my current idea below. Is this solution okay, or am I fundamentally missing something? Are there ways the code sample I gave could be improved?

suspend fun foo(
    /* other parameters here */
    eventChannel: Channel<FooEvent>,
) {
    eventChannel.send(FooEvent.BeginBar())
    try {
        /* do work that may fail here */
        eventChannel.send(FooEvent.FinishBar())
    } catch (e: Exception) {
        eventChannel.send(FooEvent.FailBar(e))
        return
    }

    eventChannel.send(FooEvent.BeginBaz())
    try {
        /* do work that may fail here */
        eventChannel.send(FooEvent.FinishBaz())
    } catch (e: Exception) {
        eventChannel.send(FooEvent.FailBaz(e))
        return
    }
}

sealed class FooEvent {

    class BeginBar : FooEvent()
    class FailBar(cause: Throwable) : FooEvent()
    class FinishBar : FooEvent()

    class BeginBaz : FooEvent()
    class FailBaz(cause: Throwable) : FooEvent()
    class FinishBaz : FooEvent()
    
}
1
  • The general idea is to use Flows, not Channels (Flows use Channels under the hood, though). When you use a reactive UI you would want to use a view model where you convert flows from your data sources into a StateFlow and pass user-originated events back. Commented Aug 3 at 17:09

1 Answer 1

2

Sounds like a use-case for Flow to me.

The sealed class structure would compliment this nicely. Just return a flow which you can collect from wherever you'd want.

fun foo() = flow {
    emit(FooEvent.BeginBar())
    // After stuff's done
    emit(FooEvent.Finish())
}
Sign up to request clarification or add additional context in comments.

2 Comments

I see, thank you for your input! I've been doing research and I see that "Channel" is for hot data (produce immediately) while "Flow" is for cold data (produce when collected). I also see "FlowChannel", but the term confuses me. Is there a guideline for choosing between Channel, Flow, and FlowChannel?
For hot data you should use a SharedFlow. Channels are only useful when you need a broadcast semantic. It looks like callbackFlow() is most useful for you. But I don't know what a "FlowChannel" is.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.