kotlinx.coroutines icon indicating copy to clipboard operation
kotlinx.coroutines copied to clipboard

Add sample operator to Flow, which can sample a Flow with the emissions of another Flow

Open adibfara opened this issue 5 years ago • 8 comments

The aim of this PR is to bring the ability to sample a flow with the emissions from another flow. I've a extension methods to Flow:

Flow<T>.sampleBy(sampler:ReceiveChannel<R>) which samples a flow whenever the receive channel emits.

Notes:

  1. The current sample has been refactored to use the sampleBy method with a simple flow of an interval.
  2. All the current test cases for sample(period) have been added for both the operators (in a new file called SampleByTest).
  3. The operator can still be called sample, but I think sampleBy is more readable/understandable.

Example of sampleBy with another Flow:

flow {
    repeat(10) {
        emit(it)
        delay(50)
    }
}.sampleBy(flow {
    repeat(10) {
        delay(100)
        emit(it)
    }
})

produces 0, 2, 4, 6, 8.

adibfara avatar Apr 28 '19 17:04 adibfara

Each ReceiveChannel can be converted to a Flow, why overload this operator for both types? Is Flow<T>.sampleBy(sampler: Flow<R>): Flow<T> enough?

fvasco avatar Apr 28 '19 17:04 fvasco

@fvasco That makes sense. I removed the channel operator with its unit tests since they can easily be converted to flows.

adibfara avatar Apr 28 '19 17:04 adibfara

Can you, please, clarify what is the use-case for sampleBy? In what kind of circumstance and applications it might be needed?

elizarov avatar Apr 28 '19 21:04 elizarov

sampleBy operator is the more generalized version of the sample operator, which is not bound to time. The user of this operator can specify any kind of flow emission for the sampling to work. Its application is synchronizing (and restrict) events from an observable source, with another. image RxJava implementation of sample

One simple use case for this operator would be the Refresh Button in Gmail when there's new mails available:

  1. A flow which emits data whenever the data is updated

  2. A Refresh button that exposes a flow for its click events.

  3. We want our UI to be updated according to the latest received data, whenever the user presses the refresh button.

These two flows cannot be:

  1. combinedLatest with each other, since If the user has clicked on the refresh button, on every emission of data afterwards, the page would suddenly get updated.

  2. zip with each other, aside from the previous problem, if the user clicks on the refresh button multiple times, the UI would not get updated if the data is not received in the mean time..

adibfara avatar Apr 29 '19 07:04 adibfara

I renamed the operator to sample, since sampleBy was kind of a new name (it made no sense, since If we are doing that, other operators such as flatMap should become flatMapBy).

Also, I've updated the code to use the new flowScope and renamed the the unit test functions to match the original ones.

adibfara avatar Jun 08 '19 14:06 adibfara

@elizarov does this PR need further improvements? I'm asking since it becomes a stale/conflicting branch after a few days.

adibfara avatar Jun 09 '19 08:06 adibfara

We have not reached consensus yet on whether we should add sampleBy to the core library in the first place, so please hold on.

elizarov avatar Jun 09 '19 14:06 elizarov

@elizarov Since this PR is still open (which is appreciated, after 4 years 😅), I've updated it with master so we can decide on merging or closing it. Please do let me know if it needs anything else to make it reach the decision.

Just to reiterate this PR's purpose, it generalizes the sample operator to accept a flow as its input (instead of just a time), so users can sample their flows with other flows (e.g. emit my last value when the other emits).

Thanks.

adibfara avatar Jul 11 '23 16:07 adibfara