4

Using C# with Rx.NET. Is there a way to extend the duration time of an Observable.Timer after it was started? Is there a way with Join(...) or Expand(...)? I do not like to dispose the old timer and not like to create a new one.

Example: initial duration is 5 minutes. But 1 minute before the timer completes, I like to reset it to 5 minutes, to measure 5 minutes again.

var _timerDisposable = Observable.Timer(duration)
.ObserveOn(Scheduler.Default)
.Subscribe((_) => DoSomething());
this.myself
  • 2,101
  • 2
  • 22
  • 36

2 Answers2

7

This is similar to @Krzystztof's answer, but more idiomatic to Rx:

var DoSomething = new Action(() => {});
var duration = TimeSpan.FromSeconds(5);
var resetSignal = new Subject<Unit>();


var scheduler = Scheduler.Default;
resetSignal
    .Select(_ => Observable.Timer(duration))
    .Switch()
    .ObserveOn(scheduler)
    .Subscribe(_ => DoSomething())

// to reset or start timer:
resetSignal.OnNext(Unit.Default);

Explanation:

Every time resetSignal produces a value, a new Timer is started. Next, the Switch operator automatically switches to the latest observable (in our case the latest timer), effectively ignoring old ones, when a new one pops out. Under the covers, Switch cleanly disposes the old subscription to the previous observable (timer), and subscribes to the new observable.

Shlomo
  • 14,102
  • 3
  • 28
  • 43
  • I like this answer, but Rx is not the easiest thing to figure out if you're trying to read code. My rule is that easy reading takes priority over being clever. The need for an explanation below is sufficient to make this something I would want to think twice about before putting in the production code. – theMayer Jan 25 '19 at 04:51
  • 1
    This is precisely the code that should be used for production code. Please check the [gold badge](https://stackoverflow.com/help/badges/5900/system-reactive) holders for [system.reactive] to confirm that I know what I'm talking about. – Enigmativity Jan 25 '19 at 04:56
  • 1
    @theMayer ...and to a PHP developer, I'm sure a string-built SQL query reads easier than a EF query. That doesn't mean there's a choice of easy reading vs clever: There's an objectively optimal answer here and another one laden with pit falls. If one finds my solution hard to read, I would suggest they work to understand it. – Shlomo Jan 25 '19 at 06:42
  • 1
    Nice, I also like it more than mine. Just as a side note, this won't start automatically, you need to tick reset signal or add `StartWith(Unit.Default)` before the select. – Krzysztof Skowronek Jan 25 '19 at 10:51
  • 1
    @Shlomo EF is another disaster in and of itself- not sure why you’d bring that up. Again, not saying this isn’t a good answer, it’s just not a terribly pragmatic answer. For example, the `System.Timers.Timer` would be much preferred over this, in my opinion. – theMayer Jan 25 '19 at 12:28
  • @Krzysztof thanks, clarified in comment – Shlomo Jan 25 '19 at 14:43
  • 1
    @theMayer I am with you, readability over cleverness. When you encapsulate the code in well named classes and methods, it will be clear to a programmer what the code does. – this.myself Jan 30 '19 at 10:06
1

The correct action here is to dispose of the subscription, then create a new observable sequence. By disposing, you will unsubscribe from the timer and will not receive a notification when the duration has expired. Note that it is unnecessary to indicate the default scheduler.

  _timerSubscription.Dispose();
  _timerSubscription = Observable.Timer(duration)                                   
                                 .Subscribe((_) => DoSomething());

--

Editorial: There are many ways to do what you ask. Since there's no broader context presented, this is probably the simplest way. Approaches suggested by others, while technically correct, may be confusing and hard to interpret, but may also work better in your use case. In the future, a more complete example or description of the issue may yield better results.

theMayer
  • 15,456
  • 7
  • 58
  • 90
  • 1
    Actually you dispose of the Subscription - not Observable. You don't have to build the Observable again, just subscribe to it. – supertopi Jan 23 '19 at 10:16
  • That’s correct- – theMayer Jan 23 '19 at 12:43
  • No, this isn't the correct way of doing it. Shlomo is correct with using `.Switch()`. – Enigmativity Jan 25 '19 at 04:38
  • @Enigmativity - 3 lines vs. 10 - I'll take mine any day of the week :) – theMayer Jan 25 '19 at 04:44
  • 2
    @theMayer - Your two lines don't play well with things like the `.Publish` operator or any operator that shares subscriptions. Shlomo's answer does. You're also spreading your code around your class. It's not readable because of that. Also, there is a `SerialDisposable` class that can reduce this to one line - but it doesn't make it the best approach. – Enigmativity Jan 25 '19 at 04:48
  • @Enigmativity I updated the answer to reflect that there is more than one way to skin the cat – theMayer Jan 25 '19 at 05:02