RxSwiftCommunity / RxSwiftExt

A collection of Rx operators & tools not found in the core RxSwift distribution
MIT License
1.33k stars 213 forks source link

Pause Observable #162

Open snowtema opened 6 years ago

snowtema commented 6 years ago

Name and description

I have a timer Observable and I need to pause it with one condition: when it'll resume it'll continue to emit elements from an element, which be paused with initial delay.

Example of use

My code:

let timerActiveSubject = BehaviorSubject<Bool>(value: true)
let timer = Observable<Int>
    .timer(0, period: 1, scheduler: MainScheduler.instance)
    .pausable(timerActiveSubject)

timer
    .subscribe(onNext: { (sec) in
        debugPrint("Timer tick: \(sec)")
    })
    .disposed(by: self.bag)

...

timerActiveSubject.onNext(false)

...

// When I send `true` element to subject, timer observable resume, 
//   but emitted all leaved elements in moment without delay. 
timerActiveSubject.onNext(true) 
freak4pc commented 6 years ago

@snowtema Did you look into pausableBuffered ?

snowtema commented 6 years ago

@freak4pc Yes, I used both forms but it also didn't work for my case. I have a countdown timer observable and when I clicked on the button to pause observable it pausing, but when I clicked one more time on the button for continue, observable emits all skipped elements in one moment and continue to emit next elements in the normal case. In detail, I paused observable when it was on 10 sec, waiting 5 seconds and continue and instead of getting 9, 8 and other elements it emits 9-6 sec in one moment and continues 5 sec and etc.

2018-07-08 22 08 08

2018-07-08 22 21 51

func countDownPausableTimer(from: Int, to: Int, quickStart: Bool) -> Observable<Int> {
    return Observable<Int>
        .timer(quickStart ? 0 : 1, period: 1, scheduler: MainScheduler.instance)
        .take(from - to + 1)
        .pausableBuffered(self.timerActiveSubject, limit: nil)
//        .pausable(self.timerActiveSubject)         it also didn't work
        .takeWhile { _ in
            return !self.forceSkip
        }
        .map { from - $0 }
}

self.forceSkip = false
self.timerActiveSubject.onNext(true)
self.countDownPausableTimer(from: duration, to: 0, quickStart: true)
    .subscribe(onNext: { (sec) in
        debugPrint("Timer tick: \(sec)")
        self.timesSubject.onNext("\(sec) sec")
    }, onCompleted: {
        self.timerActiveSubject.onNext(false)
        self.buttonEnabledSubject.onNext(true)
    })
    .disposed(by: self.bag)
freak4pc commented 6 years ago

Yup, that's how pausableBuffered works.

The problem with an operator like you're suggesting is that it "lies" about when events are emitted. It means that the stream isn't only paused but is also actually delaying additional emitted items further down the stream.

I'm not sure how of a popular use case this would be in order to add an operator here, and I feel like we need more community involvement to see if other people would need this enough. What are your thoughts?

snowtema commented 6 years ago

So, what are the most cases for these operators than if it can't pause the initial stream?

I agree with you, we need community help.

bobgodwinx commented 6 years ago

@freak4pc I think we should add it a good idea 💡very useful for a jugging and F1 apps 😁

freak4pc commented 6 years ago

@snowtema The issue is that "pausing" just buffers values. What you're asking is that it would buffer the value along with its "time", and then when it emits the buffered elements, it needs to buffer them according to these "time intervals" - which seems rather complicated to me.

I wouldn't want to embark on adding such a complex operator in RxSwiftExt unless there's a very wide need/response for it, but that's just my personal opinion :-)

snowtema commented 6 years ago

@freak4pc Well, maybe these operators need to be renamed? :) Because now their names differ with their behaviour and it would mislead.

freak4pc commented 6 years ago

@snowtema I don't think so ? The emissions are paused and buffered, and then the buffer is "flushed" after un-paused. I don't think the naming is misleading, you might be adding a different meaning to it which is fine - but not conflicting with the fact the name is correct:)