Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot return IObservable<T> from a method marked async

This contrived example is roughly how my code is structured:

public abstract class SuperHeroBase
{
    protected SuperHeroBase() { }

    public async Task<CrimeFightingResult> FightCrimeAsync()
    {
        var result = new CrimeFightingResult();

        result.State = CrimeFightingStates.Fighting;

        try
        {
            await FightCrimeOverride(results);
        } 
        catch
        {
            SetError(results);
        }

        if (result.State == CrimeFightingStates.Fighting)
            result.State = CrimeFightingStates.GoodGuyWon;

        return result;
    }

    protected SetError(CrimeFightingResult results)
    {
        result.State = CrimeFightingStates.BadGuyWon;
    }

    protected abstract Task FightCrimeOverride(CrimeFightingResult results);
}

public enum CrimeFightingStates
{
    NotStarted,
    Fighting,
    GoodGuyWon, // success state
    BadGuyWon // error state
}

public class CrimeFightingResult
{
    internal class CrimeFightingResult() { }

    public CrimeFightingStates State { get; internal set; }
}

Now I'm trying to build a collection that would hold multiple SuperHero objects and offer a AllHerosFightCrime method. The hero's should not all fight at once (the next one starts when the first is finished).

public class SuperHeroCollection : ObservableCollection<SuperHeroBase>
{
    public SuperHeroCollection() { }

    // I mark the method async...
    public async IObservable<CrimeFightingResult> AllHerosFightCrime()
    {
        var heros = new List<SuperHeroBase>(this);

        var results = new ReplaySubject<CrimeFightingResult>();

        foreach (var hero in heros)
        {
            // ... so I can await on FightCrimeAsync and push 
            // the result to the subject when done
            var result = await hero.FightCrimeAsync();
            results.OnNext(result);
        }

        results.OnCompleted();

        // I can't return the IObservable here because the method is marked Async.
        // It expects a return type of CrimeFightingResult
        return results;
    }
}

How can I return the IObservable<CrimeFightingResults> and still have the call to FightCrimeAsync awaited?

like image 906
mbursill Avatar asked Oct 03 '22 06:10

mbursill


1 Answers

You could turn your task into an observable and combine them using Merge:

public IObservable<CrimeFightingResult> AllHerosFightCrime()
{
    var heros = new List<SuperHeroBase>(this);
    return heros.Select(h => h.FightCrimeAsync().ToObservable())
        .Merge();
}

If you want to maintain the order your events are received you can use Concat instead of Merge.

like image 172
Lee Avatar answered Oct 18 '22 04:10

Lee