我有一个async
方法,它是一个长时间运行的方法,它会读取一个流,当它发现某个事件触发事件时:
public static async void GetStream(int id, CancellationToken token)
它需要取消令牌,因为它是在新任务中创建的。在内部,它在读取流时调用await
:
var result = await sr.ReadLineAsync()
现在,我想将此转换为返回IObservable<>的方法这样我就可以在反应式扩展中使用它。根据我的阅读,最好的方法是使用Observable.Create
,因为RX 2.0现在也支持异步,所以我可以使用这样的东西:
public static IObservable<Message> ObservableStream(int id, CancellationToken token)
{
return Observable.Create<Message>(
async (IObserver<Message> observer) =>
{
里面的其余代码是相同的,但不是触发我正在调用observer.OnNext()
的事件。但是,这感觉不对。有一件事我在那里混合了CancellationTokens,虽然添加了async关键字使它工作,这实际上是最好的事情吗?我正在调用我的ObservableStream:
Client.ObservableStream(555404, token).ObserveOn(Dispatcher.CurrentDispatcher).SubscribeOn(TaskPoolScheduler.Default).Subscribe(m => Messages.Add(m));
答案 0 :(得分:11)
你是对的。通过IObservable
代表您的界面后,您应避免要求来电者提供CancellationToken
。这并不意味着你不能在内部使用它们。 Rx提供了几种生成CancellationToken
实例的机制,当观察者从您的observable取消订阅时,这些实例被取消。
有很多方法可以解决您的问题。最简单的几乎不需要更改代码。它使用Observable.Create
的重载,为您提供CancellationToken
,如果调用者取消订阅,则会触发:{/ p>
public static IObservable<Message> ObservableStream(int id)
{
return Observable.Create<Message>(async (observer, token) =>
{
// no exception handling required. If this method throws,
// Rx will catch it and call observer.OnError() for us.
using (var stream = /*...open your stream...*/)
{
string msg;
while ((msg = await stream.ReadLineAsync()) != null)
{
if (token.IsCancellationRequested) { return; }
observer.OnNext(msg);
}
observer.OnCompleted();
}
});
}
答案 1 :(得分:1)
你应该改变GetStream来返回一个Task,而不是void(返回async void不好,除非绝对需要,正如svick所评论的那样)。一旦你返回一个任务,你就可以调用.ToObservable(),你就完成了。
例如:
public static async Task<int> GetStream(int id, CancellationToken token) { ... }
然后,
GetStream(1, new CancellationToken(false))
.ToObservable()
.Subscribe(Console.Write);