.net 如何使用反应式框架将两个异步操作链接在一起?

.net 如何使用反应式框架将两个异步操作链接在一起?,.net,asynchronous,system.reactive,.net,Asynchronous,System.reactive,我真正想做的就是完成两个异步操作,一个接一个。例如 下载网站X。完成后,下载网站Y。从两个观察对象中进行SelectMany(使用ToAsync将操作作为IObservables获取),就像LINQ SelectMany(或使用语法糖)一样: 还有其他选择,但这取决于您的具体情况。我自己不喜欢这样的建议,但是 如果您需要按顺序执行两个操作,请按顺序执行(您仍然可以在与main不同的线程上执行) 如果您仍然希望在代码中分离任务,那么新构造与System.Parallel是合适的: var task

我真正想做的就是完成两个异步操作,一个接一个。例如


下载网站X。完成后,下载网站Y。

从两个观察对象中进行SelectMany(使用ToAsync将操作作为IObservables获取),就像LINQ SelectMany(或使用语法糖)一样:


还有其他选择,但这取决于您的具体情况。

我自己不喜欢这样的建议,但是

如果您需要按顺序执行两个操作,请按顺序执行(您仍然可以在与main不同的线程上执行)

如果您仍然希望在代码中分离任务,那么新构造与System.Parallel是合适的:

var task1 = Task.Factory.StartNew (() => FirstTask());
var task2 = task1.ContinueWith (frst => SecondTask ());

如果这是一种破坏系统反应性的方法,那么试试Observable.GenerateInSequence——但这肯定是过火了。记住,observable是enumerable的对应项,最好以类似的方式使用(对数据进行迭代)。它不是运行异步操作的工具

编辑:我想承认我错了,理查德是对的——在我回复时,我对RX不太满意。现在我认为RX是启动异步操作最自然的方式。然而,理查德的回答有点肤浅,应该是:

var result =  
from x in XDownload.ToAsync()() 
from y in YDownload.ToAsync()() 
select y   

也许是以下几点:

    static IObservable<DownloadProgressChangedEventArgs> CreateDownloadFileObservable(string url, string fileName)
    {
       IObservable<DownloadProgressChangedEventArgs> observable =
           Observable.CreateWithDisposable<DownloadProgressChangedEventArgs>(o =>
        {
            var cancellationTokenSource = new CancellationTokenSource();
            Scheduler.TaskPool.Schedule
            (
                () =>
                {

                    Thread.Sleep(3000);
                    if (!cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        WebClient client = new WebClient();
                        client.DownloadFileAsync(new Uri(url), fileName,fileName);


                        DownloadProgressChangedEventHandler prgChangedHandler = null;
                        prgChangedHandler = (s,e) =>
                            {                                                   
                                o.OnNext(e);
                            };


                        AsyncCompletedEventHandler handler = null;
                        handler = (s, e) =>
                        {
                            prgChangedHandler -= prgChangedHandler;                            
                            if (e.Error != null)
                            {
                                o.OnError(e.Error);
                            }
                            client.DownloadFileCompleted -= handler;
                            o.OnCompleted();
                        };
                        client.DownloadFileCompleted += handler;
                        client.DownloadProgressChanged += prgChangedHandler;
                    }
                    else
                    {
                        Console.WriteLine("Cancelling download of {0}",fileName);
                    }
                }
            );
            return cancellationTokenSource;
        }
        );
       return observable;
    }

    static void Main(string[] args)
    {

        var obs1 = CreateDownloadFileObservable("http://www.cnn.com", "cnn.htm");
        var obs2 = CreateDownloadFileObservable("http://www.bing.com", "bing.htm");

        var result = obs1.Concat(obs2);
        var subscription = result.Subscribe(a => Console.WriteLine("{0} -- {1}% complete ",a.UserState,a.ProgressPercentage), e=> Console.WriteLine(e.Message),()=> Console.WriteLine("Completed"));
        Console.ReadKey();
        subscription.Dispose();
        Console.WriteLine("Press a key to exit");
        Console.ReadKey();
}

“它不是一个运行异步操作的工具。”我完全不同意这种说法,延续monad(Rx)正是我们合成异步操作所需要的,因为我们显式地传递任何状态,并且所有的副作用都得到了管理。首先创建Rx的主要原因之一是异步和并行编程。从可观察管道创建异步操作-是。使用Observable启动异步任务(特别是一个或两个)而不观察结果-这将过于复杂。是的,Rx的全部要点是异步操作的可组合性。我刚刚完成了一个严重依赖Rx的应用程序的编写,我使用的代码与Richards的示例一模一样。在一个地方,我需要提交多达18个web服务请求来加载数据,如果没有Rx,我需要的代码将是Rx的10倍。如果“X”或“Y”多次返回,我们将开始看到交叉连接行为。它非常适合只返回一次的异步操作。但是为了安全起见,使用Prune扩展方法将只允许一个通知通过,因此对于类似webservice调用的东西是一个好主意。
    static IObservable<DownloadProgressChangedEventArgs> CreateDownloadFileObservable(string url, string fileName)
    {
       IObservable<DownloadProgressChangedEventArgs> observable =
           Observable.CreateWithDisposable<DownloadProgressChangedEventArgs>(o =>
        {
            var cancellationTokenSource = new CancellationTokenSource();
            Scheduler.TaskPool.Schedule
            (
                () =>
                {

                    Thread.Sleep(3000);
                    if (!cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        WebClient client = new WebClient();
                        client.DownloadFileAsync(new Uri(url), fileName,fileName);


                        DownloadProgressChangedEventHandler prgChangedHandler = null;
                        prgChangedHandler = (s,e) =>
                            {                                                   
                                o.OnNext(e);
                            };


                        AsyncCompletedEventHandler handler = null;
                        handler = (s, e) =>
                        {
                            prgChangedHandler -= prgChangedHandler;                            
                            if (e.Error != null)
                            {
                                o.OnError(e.Error);
                            }
                            client.DownloadFileCompleted -= handler;
                            o.OnCompleted();
                        };
                        client.DownloadFileCompleted += handler;
                        client.DownloadProgressChanged += prgChangedHandler;
                    }
                    else
                    {
                        Console.WriteLine("Cancelling download of {0}",fileName);
                    }
                }
            );
            return cancellationTokenSource;
        }
        );
       return observable;
    }

    static void Main(string[] args)
    {

        var obs1 = CreateDownloadFileObservable("http://www.cnn.com", "cnn.htm");
        var obs2 = CreateDownloadFileObservable("http://www.bing.com", "bing.htm");

        var result = obs1.Concat(obs2);
        var subscription = result.Subscribe(a => Console.WriteLine("{0} -- {1}% complete ",a.UserState,a.ProgressPercentage), e=> Console.WriteLine(e.Message),()=> Console.WriteLine("Completed"));
        Console.ReadKey();
        subscription.Dispose();
        Console.WriteLine("Press a key to exit");
        Console.ReadKey();
}
WebClient c = new WebClient();
c.CreateDownloadFileObservable ("www.bing.com","bing.htm");