2012-03-05 30 views
6

我想用Rx異步處理一些任務,例如:如何在SelectMany語句中處理來自異步方法的異常

var list = Enumerable.Range(0, 100) 
    .ToObservable() 
    .SelectMany(x => Observable.Start(() => { 
     Console.WriteLine("Processing {0} ...", x); 

     Thread.Sleep(100 * x % 3); 

     if (x > 90) { 
      Console.WriteLine("Procesing exception {0} > 90", x); 
      throw new Exception("Value too large"); 
     } 
     Console.WriteLine("Processing {0} completed.", x); 
     return x; 
    })) 
    .Subscribe(
     x => { Console.WriteLine("Next [{0}]", x); }, 
     e => { 
      Console.WriteLine("Exception:"); 
      Console.WriteLine(e.Message); 
     }, 
     () => { Console.WriteLine("Complete"); } 
    ); 

這個代碼的問題是,異常不會傳遞給訂閱者。因此,在經過大量嘗試之後,我放棄了並決定問這個簡單的問題:

如何處理在SelectMany語句中異步方法中引發的異常?

只是要說清楚,最終的實現是一個同步函數調用,可能會或可能不會拋出異常。目標是將其傳遞給用戶,以便可以進一步處理(在特定情況下會向用戶顯示一條消息)。

編輯

我感動我的調查結果下降到答案,所以,當我回答可以標記這個問題。就我個人而言,我不同意自己的回答......但有時候沒有別的辦法,所以很抱歉。

+1

這是否有助於回答您的問題? http:// stackoverflow。com/questions/7210051/catch-exceptions-which-may-be-thrown-a-subscription-onnext-action – user981225 2012-03-05 22:46:25

+0

不完全如此壓倒例外,但是,如果沒有更好的東西進來,那麼這個轉移想法可能是有用的。但是,我不確定包裝是否會在我的情況下工作,因爲我正在處理多個異步和並行呼叫......但我會調查,謝謝。 – AxelEckenberger 2012-03-05 23:16:41

+0

@ user981225,謝謝你的寶貴意見,但答案很簡單,請參閱編輯。 – AxelEckenberger 2012-03-06 11:28:35

回答

1

答案

其實代碼正常工作。然而,由於異步操作仍然在後臺執行,所以調試器會中斷異常 - 至少至少在第一次異常發生時已經啓動的那些異常操作。扔我!如果你在沒有調試器的情況下運行代碼,那麼異常會被吞下。所以我猜這個問題真的出現在了電腦前:-)

還有一些關於Observable.Start的說明,正如我所假設的 - 一個正確的 - 實現應該有實際上一些錯誤處理實施...請參閱背景。

背景

Observable.Start是使用Observable.ToAsync方法把一個功能/ acion成異步操作的便捷方法。如果你看看這個方法的實現,你會發現它已經做了異常處理/轉發。

public static Func<IObservable<TResult>> ToAsync<TResult>(this Func<TResult> function, IScheduler scheduler) { 
    if (function != null) { 
     if (scheduler != null) { 
      return() => { 
       AsyncSubject<TResult> asyncSubject = new AsyncSubject<TResult>(); 
       scheduler.Schedule(() => { 
        TResult result = default(TResult); 
        try { 
         result = function(); 
        } catch (Exception exception1) { 
         Exception exception = exception1; 
         asyncSubject.OnError(exception); 
         return; 
        } 
        asyncSubject.OnNext(result); 
        asyncSubject.OnCompleted(); 
       }); 
       return asyncSubject.AsObservable<TResult>(); 
      }; 
     } else { 
      throw new ArgumentNullException("scheduler"); 
     } 
    } else { 
     throw new ArgumentNullException("function"); 
    } 
} 
3

使用Materialize將您的OnError/OnCompleted消息轉換爲通知。

例如,

observable.SelectMany(x => Observable.Start(fn).Materialize())

,會得到錯誤/完成裹在通知中您的實際認購點一路下滑行進行處理,而不是在錯誤被終止的SelectMany內。

這對大多數異步調用操作很有用,因爲該方法失敗或完成。

+0

+1如果您有一系列操作並希望將通知推送到通用錯誤處理程序,Materialise可能會很有趣。不錯的選擇,但正如陳述的...可悲的是,問題坐在電腦前,無法使用正確的工具... :-) – AxelEckenberger 2012-03-08 12:39:36