简体   繁体   English

如何通过在原始序列的值上运行任务来创建Rx序列?

[英]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: 我需要以下语义:

  • each tasks starts after the previous item's task has finished 上一个项目的任务完成后,每个任务都会启动
  • if a task has been cancelled or faulted, it is skipped 如果任务已被取消或出现故障,则会跳过该任务
  • the order of the original sequence is strictly preserved 严格保留原始序列的顺序

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: 但它并不能满足我的需求:

  • it creates tasks as original items come up, whereas I needed to wait for each task to finish 它会在原始项目出现时创建任务,而我需要等待每项任务完成
  • it offers no option to skip canceled and faulted tasks 它没有提供跳过已取消和故障任务的选项

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. 我重用了DeferStartAsync ,同时还添加了Catch来吞下错误。 The combination of Defer and Concat ensures tasks wait for each other and start in the original order. DeferConcat的组合可确保任务彼此等待并以原始顺序开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 RX 实现超时序列? - How to achieve sequence of timeouts with RX? Rx如何从pub / sub模式创建序列 - Rx how to create a sequence from a pub/sub pattern 学习Rx:如何将可观察的字符序列解析为可观察的字符串序列? - Learning Rx: How can I parse an observable sequence of characters into an observable sequence of strings? 如何检查数字字符串是否处于运行顺序 - How do I check if a number string is in running sequence Rx框架:在超时时执行操作,而不会中断原始的可观察序列 - Rx Framework: execute an action on timeout without interrupting the original observable sequence 如何使用.net RX组织数据处理器序列 - How to organize sequence of data processors with .net RX 如何按两个值对序列进行分组并保持序列的顺序? - How can I group a sequence by two values and keep the order of the sequence? 学习Rx:如何在.Window的输出上使用.Scan获取可观察的bool值序列 - Learning Rx: How to use .Scan on the output of .Window for an observable sequence of bool values 退订后,我如何等待一切按Rx可观察的顺序进行? - How can I await that everything is done in a Rx observable sequence after unsubscribe? 如何使用Rx检索序列生成器的生命周期操作? - How to retrieve lifetime operations of a sequence generator using Rx?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM