Rxサブスクリプション内で非同期関数をコールバックしたいと思います。
例えば。そのように:
public class Consumer
{
private readonly Service _service = new Service();
public ReplaySubject<string> Results = new ReplaySubject<string>();
public void Trigger()
{
Observable.Timer(TimeSpan.FromMilliseconds(100)).Subscribe(async _ => await RunAsync());
}
public Task RunAsync()
{
return _service.DoAsync();
}
}
public class Service
{
public async Task<string> DoAsync()
{
return await Task.Run(() => Do());
}
private static string Do()
{
Thread.Sleep(TimeSpan.FromMilliseconds(200));
throw new ArgumentException("invalid!");
return "foobar";
}
}
[Test]
public async Task Test()
{
var sut = new Consumer();
sut.Trigger();
var result = await sut.Results.FirstAsync();
}
例外を正しくキャッチするために何をする必要がありますか?
async
メソッドをSubscribe
に渡したくない場合、async void
メソッドが作成されます。 async void
を避けるために最善を尽くしてください。
あなたの場合、私はthink必要なのは、シーケンスの各要素に対してasync
メソッドを呼び出し、すべての結果をキャッシュすることです。その場合、SelectMany
を使用して各要素のasync
メソッドを呼び出し、Replay
をキャッシュして(さらにConnect
を使用してボールを回転させます):
public class Consumer
{
private readonly Service _service = new Service();
public IObservable<string> Trigger()
{
var connectible = Observable.Timer(TimeSpan.FromMilliseconds(100))
.SelectMany(_ => RunAsync())
.Replay();
connectible.Connect();
return connectible;
}
public Task<string> RunAsync()
{
return _service.DoAsync();
}
}
代わりにResults
プロパティをTrigger
メソッドから返されるように変更しました。これはより理にかなっていると思うので、テストは次のようになります。
[Test]
public async Task Test()
{
var sut = new Consumer();
var results = sut.Trigger();
var result = await results.FirstAsync();
}
Paul Bettsの答えはほとんどのシナリオで機能しますが、非同期関数が完了するのを待つ間にストリームをブロックする場合は、次のようなものが必要です。
Observable.Interval(TimeSpan.FromSeconds(1))
.Select(l => Observable.FromAsync(asyncMethod))
.Concat()
.Subscribe();
または:
Observable.Interval(TimeSpan.FromSeconds(1))
.Select(_ => Observable.Defer(() => asyncMethod().ToObservable()))
.Concat()
.Subscribe();
これを次のように変更します。
Observable.Timer(TimeSpan.FromMilliseconds(100))
.SelectMany(async _ => await RunAsync())
.Subscribe();
サブスクライブは、非同期操作をObservable内に保持しません。