How to cancel an observable sequence

前端 未结 5 797
南笙
南笙 2020-12-17 21:19

I have a very simple IObservable that acts as a pulse generator every 500ms:

var pulses = Observable.GenerateWithTime(0, i => true         


        
相关标签:
5条回答
  • 2020-12-17 21:55

    You get an IDisposable instance back from subscribing. Call Dispose() on that.

    0 讨论(0)
  • 2020-12-17 22:00

    You can connect your IObservable subscription with CancellationTokenSource with the following snippet

    var pulses = Observable.GenerateWithTime(0,
        i => true, i => i + 1, i => i,
        i => TimeSpan.FromMilliseconds(500));
    
    // Get your CancellationTokenSource
    CancellationTokenSource ts = ...
    
    // Subscribe
    ts.Token.Register(pulses.Subscribe(...).Dispose);
    
    0 讨论(0)
  • 2020-12-17 22:04

    Here are two handy operators for canceling observable sequences. The difference between them is on what happens in case of cancellation. The TakeUntil causes a normal completion of the sequence (OnCompleted), while the WithCancellation causes an exceptional termination (OnError).

    /// <summary>Returns the elements from the source observable sequence until the
    /// CancellationToken is canceled.</summary>
    public static IObservable<TSource> TakeUntil<TSource>(
        this IObservable<TSource> source, CancellationToken cancellationToken)
    {
        return source
            .TakeUntil(Observable.Create<Unit>(observer =>
                cancellationToken.Register(() => observer.OnNext(default))));
    }
    
    /// <summary>Ties a CancellationToken to an observable sequence. In case of
    /// cancellation propagates an OperationCanceledException to the observer.</summary>
    public static IObservable<TSource> WithCancellation<TSource>(
        this IObservable<TSource> source, CancellationToken cancellationToken)
    {
        return source
            .TakeUntil(Observable.Create<Unit>(o => cancellationToken.Register(() =>
                o.OnError(new OperationCanceledException(cancellationToken)))));
    }
    

    Usage example:

    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    
    var pulses = Observable
        .Generate(0, i => true, i => i + 1, i => i, i => TimeSpan.FromMilliseconds(500))
        .WithCancellation(cts.Token);
    
    0 讨论(0)
  • 2020-12-17 22:09

    It is an old thread, but just for future reference, here is a simpler way to do it.

    If you have a CancellationToken, you are probably already working with tasks. So, just convert it to a Task and let the framework do the binding:

    using System.Reactive.Threading.Tasks;
    ...
    var task = myObservable.ToTask(cancellationToken);
    

    This will create an internal subscriber that will be disposed when the task is cancelled. This will do the trick in most cases because most observables only produce values if there are subscribers.

    Now, if you have an actual observable that needs to be disposed for some reason (maybe a hot observable that is not important anymore if a parent task is cancelled), this can be achieved with a continuation:

    disposableObservable.ToTask(cancellationToken).ContinueWith(t => {
        if (t.Status == TaskStatus.Canceled)
            disposableObservable.Dispose();
    });
    
    0 讨论(0)
  • 2020-12-17 22:17

    If you're using the GenerateWithTime (replaced now with Generate passing in a timespan func overload), you can replace the second parameter to evaulate the state of the cancellation token as follows:

    var pulses = Observable.Generate(0,
        i => !ts.IsCancellationRequested,
        i => i + 1,
        i => i,
        i => TimeSpan.FromMilliseconds(500));
    

    Alternatively, if your event which causes the cancellation token to be set can be converted to an observable itself, you could use something like the following:

    pulses.TakeUntil(CancelRequested);
    

    I posted a more detailed explanation at http://www.thinqlinq.com/Post.aspx/Title/Cancelling-a-Reactive-Extensions-Observable as well.

    0 讨论(0)
提交回复
热议问题