簡體   English   中英

使用異步函數訂閱可觀察序列

[英]Subscribing to observable sequence with async function

我有一個asnyc函數,我想在IObservable序列中的每個觀察時調用,一次限制傳遞到一個事件。 消費者希望飛行中不超過一條消息; 如果我理解正確的話,這也是RX合約。

考慮這個樣本:

static void Main() {
  var ob = Observable.Interval(TimeSpan.FromMilliseconds(100));
  //var d = ob.Subscribe(async x => await Consume(x));  // Does not rate-limit.
  var d = ob.Subscribe(x => Consume(x).Wait());
  Thread.Sleep(10000);
  d.Dispose();
}

static async Task<Unit> Consume(long count) {
  Console.WriteLine($"Consuming {count} on thread {Thread.CurrentThread.ManagedThreadId}");
  await Task.Delay(750);
  Console.WriteLine($"Returning on thread {Thread.CurrentThread.ManagedThreadId}");
  return Unit.Default;
}

Consume函數偽造750 ms處理時間, ob每100 ms產生一次事件。 上面的代碼有效,但在隨機線程上調用task.Wait() 如果我改為在注釋掉的第3行中訂閱, Consumeob生成事件相同的速率調用Consume (我甚至無法理解我在這個注釋語句中使用的Subscribe重載,所以它可能是無意義的)。

那么如何從可觀察序列到async函數一次正確地傳遞一個事件呢?

訂閱者不應該長時間運行,因此不支持在Subscribe處理程序中執行長時間運行的異步方法。

相反,請將您的異步方法視為單個值可觀察序列,該序列從另一個序列中獲取值。 現在你可以編寫序列,這就是Rx的設計目的。

現在你已經實現了這個飛躍,你可能會有類似@Reijher在Howto中從rx subscribe回調異步函數的東西

他的代碼細分如下。

//The input sequence. Produces values potentially quicker than consumer
Observable.Interval(TimeSpan.FromSeconds(1))
      //Project the event you receive, into the result of the async method
      .Select(l => Observable.FromAsync(() => asyncMethod(l)))
      //Ensure that the results are serialized
      .Concat()
      //do what you will here with the results of the async method calls
      .Subscribe();

在此方案中,您將創建隱式隊列。 在生產者比消費者更快的任何問題中,需要使用隊列在等待時收集值。 我個人更喜歡通過將數據放入隊列來使其顯式化。 或者,您可以明確地使用調度程序來發出信號,表明該應該是松弛的線程模型。

對於Rx新手來說,這似乎是一個流行的障礙(在訂閱處理程序中執行異步)。 有很多原因導致指導不是將它們放在您的訂戶中,例如:1。您打破了錯誤模型2.您正在混合異步模型(rx在這里,任務那里)3。訂閱是組合的消費者異步序列。 異步方法只是一個單獨的值序列,因此該視圖不能是序列的結尾,但結果可能是。

UPDATE

為了說明關於打破錯誤模型的評論,這是OP樣本的更新。

void Main()
{
    var ob = Observable.Interval(TimeSpan.FromMilliseconds(100));
    var d = ob.Subscribe(
        x => ConsumeThrows(x).Wait(),
        ex=> Console.WriteLine("I will not get hit"));

    Thread.Sleep(10000);
    d.Dispose();
}

static async Task<Unit> ConsumeThrows(long count)
{
    return await Task.FromException<Unit>(new Exception("some failure"));
    //this will have the same effect of bringing down the application.
    //throw new Exception("some failure");
}

在這里我們可以看到,如果要拋出OnNext處理程序,那么我們就不受Rx OnError處理程序的保護。 該異常將無法處理,很可能會導致應用程序失效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM