Rx and tasks - cancel running task when new task is spawned?

前端 未结 2 1333
醉话见心
醉话见心 2020-12-16 04:20

I have an user interaction scenario I\'d like to handle with Rx.

The scenario is similar to the canonical \"when user stops typing, do some work\" (usually, search

相关标签:
2条回答
  • 2020-12-16 04:50

    You can just use Observable.FromAsync which will generate tokens that are cancelled when the observer unsubcribes:

    input.Throttle(...)
         .Select(_ => Observable.FromAsync(token => CreateMyTask(..., token)))
         .Switch()
         .Subscribe(...);
    

    This will generate a new token for each unit of work and cancel it every time Switch switches to the new one.

    0 讨论(0)
  • 2020-12-16 05:02

    Do you have to work with Tasks?

    If you're happy to work purely with Observables then you can do this nicely yourself.

    Try doing something like this:

    var query =
        Observable.Create<int>(o =>
        {
            var cancelling = false;
            var cancel = Disposable.Create(() =>
            {
                cancelling = true;
            });
            var subscription = Observable.Start(() =>
            {
                for (var i = 0; i < 100; i++)
                {
                    Thread.Sleep(10); //1000 ms in total
                    if (cancelling)
                    {
                        Console.WriteLine("Cancelled on {0}", i);
                        return -1;
                    }
                }
                Console.WriteLine("Done");
                return 42;
            }).Subscribe(o);
            return new CompositeDisposable(cancel, subscription);
        });
    

    This observable is doing some hard work in the for loop with the Thread.Sleep(10);, but when the observable is disposed the loop is exited and the intensive CPU work ceases. Then you can use the standard Rx Dispose with the Switch to cancel the in progress work.

    If you'd like that bundled up in a method, then try this:

    public static IObservable<T> Start<T>(Func<Func<bool>, T> work)
    {
        return Observable.Create<T>(o =>
        {
            var cancelling = false;
            var cancel = Disposable
                .Create(() => cancelling = true);
            var subscription = Observable
                .Start(() => work(() => cancelling))
                .Subscribe(o);
            return new CompositeDisposable(cancel, subscription);
        });
    }
    

    And then call it with a function like this:

    Func<Func<bool>, int> work = cancelling =>
    {
        for (var i = 0; i < 100; i++)
        {
            Thread.Sleep(10); //1000 ms in total
            if (cancelling())
            {
                Console.WriteLine("Cancelled on {0}", i);
                return -1;
            }
        }
        Console.WriteLine("Done");
        return 42;
    };
    

    Here's my code that proved this worked:

    var disposable =
        ObservableEx
            .Start(work)
            .Subscribe(x => Console.WriteLine(x));
    
    Thread.Sleep(500);
    disposable.Dispose();
    

    I got "Cancelled on 50" (sometime "Cancelled on 51") as my output.

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