1

I want to stop stream A for exactly one notification whenever stream B fires. Both streams will stay online and won't ever complete.

A: o--o--o--o--o--o--o--o--o  
B: --o-----o--------o-------  
R: o-----o-----o--o-----o--o  

or

A: o--o--o--o--o--o--o--o--o  
B: -oo----oo-------oo-------  
R: o-----o-----o--o-----o--o  
Gajus
  • 69,002
  • 70
  • 275
  • 438
Christoph
  • 26,519
  • 28
  • 95
  • 133
  • Do you mean you want to supress stream A OnNexts while stream B OnNext is firing then continue with stream A when stream B stops? If so, how long must stream B stop for until you resume receiving notifications from stream A? Or do you want to just stop stream A for exactly 1 OnNext notification, whenever stream B sends exactly 1 OnNext notification? – Richard Anthony Hein May 08 '11 at 03:47

2 Answers2

2

This solution will work when the observable is hot (and without refCount):

streamA
    .takeUntil(streamB)
    .skip(1)
    .repeat()
    .merge(streamA.take(1))
    .subscribe(console.log);
  1. .takeUntil(streamB): make stream A complete upon stream B producing a value.
  2. .skip(1): make stream A skip one value upon starting (or as a result of .repeat()).
  3. .repeat(): make stream A repeat (reconnect) indefinitely.
  4. .merge(streamA.take(1)): offset the effect of .skip(1) at the beginning of the stream.

Example of making A stream skip every 5 seconds:

var streamA,
    streamB;

streamA = Rx.Observable
    .interval(1000)
    .map(function (x) {
        return 'A:' + x;
}).publish();

streamB = Rx.Observable
    .interval(5000);

streamA
    .takeUntil(streamB)
    .skip(1)
    .repeat()
    .merge(streamA.take(1))
    .subscribe(console.log);

streamA.connect();

You can also use this sandbox http://jsbin.com/gijorid/4/edit?js,console to execute BACTION() in the console log at the time of running the code to manually push a value to streamB (which is helpful for analysing the code).

Gajus
  • 69,002
  • 70
  • 275
  • 438
Christoph
  • 26,519
  • 28
  • 95
  • 133
  • I see ... I guess you meant streamA.Take(1) and not a.Take(1). This only works for hot observables, so you have to use Publish on your cold source observables (for those who might be testing this). – Richard Anthony Hein May 08 '11 at 14:59
  • Thanks, I corrected the typo. I guess on a cold observable, it wouldn't work without publish because everytime Repeat comes into play the stream starts again from the very first item, right? However, I haven't found so much use cases for doing such things on a cold observable. I'm still pretty new to Rx. – Christoph May 08 '11 at 15:59
  • Yes, for a cold observable it starts over. – Richard Anthony Hein May 08 '11 at 16:35
2

Here's a version of my SkipWhen operator I did for a similar question (the difference is that, in the original, multiple "B's" would skip multiple "A's"):

public static IObservable<TSource> SkipWhen<TSource, TOther>(this IObservable<TSource> source, 
    IObservable<TOther> other)
{
    return Observable.Create<TSource>(observer =>
    {
        object lockObject = new object();
        bool shouldSkip = false;

        var otherSubscription = new MutableDisposable();
        var sourceSubscription = new MutableDisposable();

        otherSubscription.Disposable = other.Subscribe(
            x => { lock(lockObject) { shouldSkip = true; } });

        sourceSubscription.Disposable = source.Where(_ =>
        {
            lock(lockObject)
            {
                if (shouldSkip)
                {
                    shouldSkip = false;
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }).Subscribe(observer);

        return new CompositeDisposable(
            sourceSubscription, otherSubscription);
    });
}

If the current implementation becomes a bottleneck, consider changing the lock implementation to use a ReaderWriterLockSlim.

Community
  • 1
  • 1
Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • Hi Richard, thank you for sharing this solution. It doesn't fit here because I don't want to skip multiple A's for multiple A's (as mentioned above). However, I keep it in mind! – Christoph May 09 '11 at 18:48
  • @Christoph - This modified version doesn't skip multiple A's for multiple B's. The _original_ did. – Richard Szalay May 09 '11 at 19:33
  • Ah, ok. But how is it different from mine except that mine is far simpler ;-) Will yours work with cold observables (mine won't) – Christoph May 09 '11 at 21:06
  • @Christoph - Yes, it will work with cold observables. Yours can be made to work with cold observables by wrapping it (minus the Subscribe) in a call to the `Publish` overload that accepts a `Func<>`. The only other problem with your implementation is that it could possible drop A values between `Repeat` subscriptions. The only way I can think of solving that is to create a custom `Subject` and use `Multicast` instead of `Publish` – Richard Szalay May 10 '11 at 06:29
  • Ah, I see. Well, I had this code working I noticed that I don't need it anyway (*sigh*). However it worked well in my basic tests. However, I accepted your answer because (at least from what you wrote) it seems to be a more general solution. – Christoph May 10 '11 at 09:50