I’m testing out the Reactive Extensions (main branch from NuGet) and I’m having some problems with the Merge. I’m running multiple operations in parallel and would like to get a notification when all the operations have completed, but I’m just not getting it.
Here’s my operation where I download a web page using WebClient and then calculate the word count:
private IObservable<int> GetWebsiteWordCount(Uri uri)
{
var client = new WebClient();
var o = Observable.FromEventPattern<DownloadStringCompletedEventArgs>(client, "DownloadStringCompleted")
.ObserveOn(Scheduler.ThreadPool)
.Select(newString => newString.EventArgs.Result.Split(' ').Length);
client.DownloadStringAsync(uri);
return o;
}
I’m then creating many of these:
var tasks = new List<IObservable<int>>()
{
GetWebsiteWordCount(new Uri("http://www.google.com", UriKind.Absolute)),
GetWebsiteWordCount(new Uri("http://www.bing.com", UriKind.Absolute)),
GetWebsiteWordCount(new Uri("http://www.yle.fi", UriKind.Absolute))
};
After which I use the Merge to combine these and try to get a notification when all of them have completed:
tasks.Merge()
.ObserveOn(SynchronizationContext.Current)
.Subscribe(x => Debug.WriteLine(x), ex => Debug.WriteLine("exception thrown"),
() => Debug.WriteLine("all ready"));
All of these “tasks” are execute correctly and I get the word counts in Debug-window as expected:
14279
672
292
But I don’t get the “all ready” message. Any ideas what I’m missing?
Update: Using Sum instead of Merge
I also tried to change the merge to this:
var result = from i in tasks.ToObservable()
from r in i
select r;
result.Sum().Subscribe(x => Debug.WriteLine("all ready. sum: " + x));
But I never got the result back.
Update: Fixing the problem with Take
Thanks to Gideon Engelberth now both the Merge and the Sum-options work. The solution was to fix the GetWebsiteWordCount-method by adding Take(1):
private IObservable<int> GetWebsiteWordCount(Uri uri)
{
var client = new WebClient();
var o = Observable.FromEventPattern<DownloadStringCompletedEventArgs>(client, "DownloadStringCompleted")
.ObserveOn(Scheduler.ThreadPool)
.Select(newString => newString.EventArgs.Result.Split(' ').Length)
.Take(1);
client.DownloadStringAsync(uri);
return o;
}
Observable.FromEventPatternnever completes as it has no way to know when there will be no more events. Because you know this particular event is the event async pattern, it should only trigger once. To tell this to the observable, add a.Take(1)somewhere in GetWebsiteWordCount