How to prevent Scan from running multiple times?

132 Views Asked by At

For example

var subject = new Subject<int>();
var test = subject.Scan(0, (x, y) => {
    Console.WriteLine("scan");
    return x + 1;
});
test.Subscribe(x => Console.WriteLine("subscribe1"));
//test.Subscribe(x => Console.WriteLine("subscribe2"));
Observable.Range(0, 1).Subscribe(subject);
Console.WriteLine("done");
Console.Read();

The output is

scan
subscribe1
done

But if you uncomment second Subscribe the output is

scan
subscribe1
scan
subscribe2
done

Why does the Scan run two times and how can I prevent it? So output should be

scan
subscribe1
subscribe2
done

I use Subject to accumulate different Observables. Then I use Scan method to update Model and then I have different places where I need to subscribe to Model updates. Maybe is there better solution without using Subject?

2

There are 2 best solutions below

5
On BEST ANSWER

Try using Observable.Publish to get an IConnectableObservable<T>.

var subject = new Subject<int>();
var test = subject
    .Scan(0, (x, y) => {
        Console.WriteLine("scan");
        return x + 1;
    })
    .Publish();
test.Subscribe(x => Console.WriteLine("subscribe1"));
test.Subscribe(x => Console.WriteLine("subscribe2"));
test.Connect();
Observable.Range(0, 1).Subscribe(subject);
Console.WriteLine("done");
Console.Read();

Output:

scan
subscribe1
subscribe2
done

Publish turns the cold Scan observable into a hot observable that begins emitting values when Connect is called.

4
On

The problem you are seeing is that Subject is a hot observable while Scan creates a new cold observable every time you subscribe to it.

Try moving scan before the subject instead

var subject = new Subject<int>();
subject.Subscribe(x => Console.WriteLine("subscribe1"));
subject.Subscribe(x => Console.WriteLine("subscribe2"));
Observable.Range(0, 1).Scan(0, (x, y) => {
    Console.WriteLine("scan");
    return x + 1;
}).Subscribe(subject);
Console.WriteLine("done");
Console.Read();

You can also do it without the Subject:

var test = Observable.Range(0, 1).Scan(0, (x, y) => {
    Console.WriteLine("scan");
    return x + 1;
}).Publish();

test.Subscribe(x => Console.WriteLine("subscribe1"));
test.Subscribe(x => Console.WriteLine("subscribe2"));
test.Connect();

Console.WriteLine("done");
Console.Read();

Hot vs Cold Observables