[英]How do I create an Rx sequence by running tasks over original sequence's values?
I have a sequence of type IObservable<T>
and a function that maps T, CancellationToken
to a Task<U>
. 我有一个
IObservable<T>
类型的序列和一个将T, CancellationToken
映射到Task<U>
的函数。 What's the cleanest way of getting an IObservable<U>
out of them? 从它们中获取
IObservable<U>
最简洁的方法是什么?
I need the following semantics: 我需要以下语义:
Here's the signature as I see it: 这是我看到的签名:
public static IObservable<U> Select<T, U> (
this IObservable<T> source,
Func<T, CancellationToken, Task<U>> selector
);
I haven't written any code yet but I will unless someone beats me to it. 我还没有写任何代码,但我会除非有人打败我。
In any case, I'm not familiar with operators like Window
, so my solution will likely be less elegant. 在任何情况下,我都不熟悉像
Window
这样的运算符,所以我的解决方案可能不那么优雅。
I need the solution in C# 4, but C# 5 answers are also welcome for the sake of comparison. 我需要C#4中的解决方案,但为了比较,也欢迎C#5答案。
If you're curious, below is my real-world scenario, more or less: 如果你很好奇,下面是我的真实场景,或多或少:
Dropbox.GetImagesRecursively ()
.ObserveOn (SynchronizationContext.Current)
.Select (DownloadImage)
.Subscribe (AddImageToFilePicker);
This seems to work for me so far: 到目前为止,这似乎对我有用:
public static IObservable<U> Select<T, U> (
this IObservable<T> source,
Func<T, CancellationToken, Task<U>> selector)
{
return source
.Select (item =>
Observable.Defer (() =>
Observable.StartAsync (ct => selector (item, ct))
.Catch (Observable.Empty<U> ())
))
.Concat ();
}
We map a deferred task-based exception-swallowing observable to each item, and then concat them. 我们将一个延迟的基于任务的异常吞咽可观察对象映射到每个项目,然后将它们连接起来。
My thought process went like this. 我的思维过程就像这样。
I noticed that one of the SelectMany
overloads does almost exactly what I wanted and even has exactly the same signature. 我注意到其中一个
SelectMany
重载几乎完全符合我的要求,甚至具有完全相同的签名。 It didn't satisfy my needs though: 但它并不能满足我的需求:
I looked at this overload's implementation and noticed it uses FromAsync
to handle task creation and cancellation: 我查看了这个重载的实现,发现它使用
FromAsync
来处理任务创建和取消:
public virtual IObservable<TResult> SelectMany<TSource, TTaskResult, TResult> (IObservable<TSource> source, Func<TSource, CancellationToken, Task<TTaskResult>> taskSelector, Func<TSource, TTaskResult, TResult> resultSelector)
{
return SelectMany_<TSource, TTaskResult, TResult> (
source,
x => FromAsync (ct => taskSelector (x, ct)),
resultSelector
);
}
I turned my eye to FromAsync
to see how it was implemented, and was pleasantly surprised to find it was composable as well: 我把目光转向了
FromAsync
以了解它是如何实现的,并且惊喜地发现它也是可组合的:
public virtual IObservable<TResult> FromAsync<TResult> (Func<CancellationToken, Task<TResult>> functionAsync)
{
return Defer (() => StartAsync (functionAsync));
}
I reused Defer
and StartAsync
, while also adding Catch
to swallow errors. 我重用了
Defer
和StartAsync
,同时还添加了Catch
来吞下错误。 The combination of Defer
and Concat
ensures tasks wait for each other and start in the original order. Defer
和Concat
的组合可确保任务彼此等待并以原始顺序开始。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.