2017-07-20 7 views
-1

私の理解ではshould be avoidedasync voidこととActionで使用した場合async() =>がちょうどasync void変装であるということであるRxの中()=>非同期を使用します。最後にパラメータとしてActionを受け入れるため、代替最後に

はしたがって、async() =>と非同期the Rx.NET Finally operatorを使用することは避けるべきである:これは悪い習慣である場合

IObservable<T>.Finally(async() => 
{ 
    await SomeCleanUpCodeAsync(); 
}; 

はしかし、その後、私がする必要がある場合に使用することをお勧めするものです非同期にネットワークを閉じるonOrrorまたはOnCompletedの可観測性があれば完了しますか?

+2

あなたがここに示されてきた非同期ラムダは '非同期void'方法ですが、すべての非同期ラムダは' void'方法であることを行っているわけではありません。 'Task'が返すメソッドが期待されるコンテキストで非同期ラムダを提供すると、それが得られます。 – Servy

答えて

1

私の理解では、非同期ボイドは避け、async () =>はちょうどasync voidです。

これは部分的に間違っています。 async() =>のいずれかを行うことができ、一致Func<Task>(良い)またはAction(悪いです)。良い/悪いの主な理由は、async void呼び出しで発生する例外がプロセスをクラッシュさせ、一方async Task例外はキャッチ可能であるということです。

だから我々はちょうどObservable.FinallyようActionの代わりにFunc<Task>にかかるAsyncFinallyオペレータ記述する必要があります。あなたはスワップアウトした場合

try 
{ 
    Observable.Interval(TimeSpan.FromMilliseconds(100)) 
     .Take(10) 
     .AsyncFinally(async() => 
     { 
      await Task.Delay(1000); 
      throw new NotImplementedException(); 
     }) 
     .Subscribe(i => Console.WriteLine(i)); 
} 
catch(Exception e) 
{ 
    Console.WriteLine("Exception caught, no problem"); 
} 

public static class X 
{ 
    public static IObservable<T> AsyncFinally<T>(this IObservable<T> source, Func<Task> action) 
    { 
     return source 
      .Materialize() 
      .SelectMany(async n => 
      { 
       switch (n.Kind) 
       { 
        case NotificationKind.OnCompleted: 
        case NotificationKind.OnError: 
         await action(); 
         return n; 
        case NotificationKind.OnNext: 
         return n; 
        default: 
         throw new NotImplementedException(); 
       } 
      }) 
      .Dematerialize() 
     ; 
    } 
} 

そして、ここでは、使用のデモだがAsyncFinallyFinallyにすると、プロセスがクラッシュします。

+0

私はこのアプローチが本当に好きです。私はMaterialize/Dematerializeに慣れていませんでした。それらを使ううまい方法。 SelectManyを使うのはなぜですか? –

+2

選択はそこで動作しません。 selectを使った非同期セレクタ関数は 'IObservable >'から 'IObservable >'に変換します。 SelectManyバージョンはそのタスクの部分を平坦化します。 – Shlomo

+0

サブスクリプションの廃棄OnCompletedまたはOnErrorが発生しないため、SomeCleanUpCodeAsyncが実行されません。私はそれができると思った。私は何か間違っているのか、回避策はありますか? –

1

他の場所と同様にRxにあります。疫病のようにasync voidを避けてください。この資料に記載されている問題に加えて、同期演算子で非同期コードを使用すると、Rxが「中断」します。

OnErrorResumeNext()を非同期でリソースをクリーンアップすることを検討したいと思います。あなたはそれが終わった最初にかかわらず、理由の後に実行される観測可能な指定OnErrorResumeNext()みましょう:

var myObservable = ... 

myObservable 
    .Subscribe(/* Business as usual */); 

Observable.OnErrorResumeNext(
     myObservable.Select(_ => Unit.Default), 
     Observable.FromAsync(() => SomeCleanUpCodeAsync())) 
    .Subscribe(); 

myObservableは、好ましくは、複数のサブスクリプションを防ぐために(例えばPublish()ConnectableObservableだろう。

+0

これは、OnErrorのシナリオを処理します。しかし、私はまた、OnCompletedの後にクリーンアップする必要があります。しかし、Observable.FromAsync()を使用していることに興味があります。これは '非同期void'を回避する方法ですか? –

+1

名前とは逆に、 'OnErrorResumeNext()'は、最初の観測可能なエラーの有無に関わらず、2番目のオブザーバブルを最初のものと実際に連結します。 'OnErrorResumeNext()'は 'Concat()'とよく似た動作をしますが、* OnError * **と** * OnCompleted * –

+0

の両方の次のobservableに移動します。それを調べます。 –

1

Finally方法署名が作用しないタスクを期待

public static IObservable<TSource> Finally<TSource>(
    this IObservable<TSource> source, 
    Action finallyAction 
) 

あります。

補足として、async voidの代わりに非同期に何かを実行する場合は、メソッド内にTask.Factoryメソッドを使用して、意図が明示的になるようにしてください。