16

I want to perform an async call based for each event raised by a Reactive Extensions Observable. I'm also trying to keep everything synchronized as I want the async call to finish before the next event is handled.

How would one go about doing something similar to the following? I say similar as the code below does not compile.

settingsChangedInMemory
    .Subscribe(async _ => {
        var settings = Extract();
        await SaveSettings(settings);
    });

I'm not sure if it changes anything, but I would need to Subscribe to multiple Observables. For example another subscription like this.

settingsChangedOnDisk
    .Subscribe(async _ => {
        var settings = await ReadSettings(settings);
        Apply(settings);
    });

How would you use Reactive Extensions to do this?

Daniel Little
  • 16,975
  • 12
  • 69
  • 93

2 Answers2

21

How about:

settingsChangedInMemory
    .SelectMany(async _ => await SaveSettings(Extract()))
    .Subscribe(x => Apply(x));

Never put an async in a Subscribe, you always want to put it in a SelectMany instead.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • Is this still true as of RX 2.0 (which predates your answer)? The above code now seems to compile and there's an example from the RX team here http://blogs.msdn.com/b/rxteam/archive/2012/03/12/reactive-extensions-v2-0-beta-available-now.aspx of a Subscribe(async () => ...) – Polemarch Sep 14 '15 at 17:11
  • 2
    Absolutely still true – Ana Betts Sep 14 '15 at 20:30
  • 9
    Do you mind elaborating Paul? They explicitly state they support async in subscribe, and your response doesn't really explain why the developers of Reactive are wrong. – Austin Salgat Jun 23 '16 at 19:28
  • `async void` methods can be called anywhere `void` methods can be called. Therefore `Subscribe` supports async methods because the methods are void. You can't await a Disposable/Subscription anyways. If you need something like that, use `SelectMany`, etc, first, and then subscribe. – cwharris Dec 12 '17 at 17:10
  • 2
    Can the `async` safely go in a `Select`, as well as in a `SelectMany`? – Stuart Hallows Jan 31 '18 at 06:05
  • 1
    @StuartHallows no it can't, select doesn't have an overload that accepts a Task – Rob May 18 '18 at 10:00
2

You can use the new ForEachAsync method released in Reactive Extensions (Rx) 2.0 like so:

await observable
    .ForEachAsync(async x =>
    {
        Console.WriteLine(x);
        await Task.Delay(1000);
    });

ForEachAsync returns a Task<T> which completes when the observable completes. More information in my blog post here or this blog post by the reactive extensions team.

Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311