Я не знаю, как вам, но мне лично (да и не только мне) работа с шаблоном IAsyncResult всегда доставляла головную боль. Все эти BeginOperationName, EndOperationName, Callbacks… Обработка ошибок там…
Нет, я не говорю что IAsyncResult – это плохо. Это хорошо и полезно и всё такое, просто работать с этим достаточно сложно.
Было сложно, пока не появился Rx 
Что нам нужно чтобы, например, асинхронно получить данные с веб-сервиса или веб-странички?
Да практически ничего:
1: public static class WebRequestExtensions
2: {
3: public static IObservable<WebResponse> ToObservable(this WebRequest request)
4: {
5: if (request == null) throw new ArgumentNullException("request");
6:
7: var getResponse = Observable
8: .FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse);
9:
10: return getResponse();
11: }
12:
13: public static string GetContentAsString(this WebResponse response)
14: {
15: if (response == null) throw new ArgumentNullException("response");
16:
17: using (var stream = response.GetResponseStream())
18: using (var reader = new StreamReader(stream, false))
19: return reader.ReadToEnd();
20: }
21:
22: }
Вся “магия” заключена в методе ToObservable(..), конкретно – в строках 7 и 8.
Вот и всё! Это действительно всё, что нам нужно сделать для работы с IAsyncResult. Следующий метод – GetContentAsString(..) – просто вспомогательный метод, позволяющий получить строку ответа из потока, я привёл его просто потому, что буду его использовать дальше в примере:
1: public static IObservable<string> GetObservableWebContent(string url)
2: {
3: var request = WebRequest.Create(new Uri(url));
4:
5: return request
6: .ToObservable()
7: .Select(x => x.GetContentAsString());
8: }
Итак, теперь для любого URL мы можем получить IObservable, который будет “уведомлен” когда результат будет готов. Причём результат мы уже имеем в виде строки, то есть, он готов к обработке.
Для начала просто выведем его на экран:
1: public static void PrintContentAsync(string url)
2: {
3: var observable = GetObservableWebContent(url)
4: .Subscribe(x => { Console.WriteLine(x); });
5: }
Но стоп, что если URL неправильный? Или сервер не отвечает? Или происходит ещё что-то неожиданное?
Тогда нам просто нужно сделать обработку ошибок:
1: public static void PrintContentCatchError(string url)
2: {
3: GetObservableWebContent(url)
4: .Subscribe(
5: x => { Console.WriteLine(x); },
6: e => { throw e; });
7: }
Здесь я в строке 6 передаю Action-обработчик исключения. Я-то просто выкидываю это исключение, но вы-то можете поступить как угодно! Более умно, например
Библиотека Rx содержит метод Catch<TException>(..), позволяющий обработать исключение без разрывания цепочки (например, можно попробовать отследить таким образом какой-нибудь SessionTimeout, возобновить сессию и повторить попытку).
Но что если, даже имея такой вот замечательный observer я всё же хочу выполнить операцию синхронно? Что если мне нужно здесь и сейчас дождаться получения результата, заморозить UI, остановить время и мир до тех пор, пока ценные данные не будут получены?
В Случае с IAsyncResult я мог просто сделать что-то вроде: var result = EndOperationName(BeginOperationName(…));
Но и здесь всё гораздо проще (строка номер 4):
1: public static void PrintContentSync(string url)
2: {
3: var content = GetObservableWebContent(url)
4: .Single();
5:
6: Console.WriteLine(content);
7: }
Словом, при наличии IObservable жизнь становится намного проще и легче, чем при наличии просто event’ов, IAsyncResult’ов и т.д.
Причём немаловажно то, что когда у меня есть IObservable я просто пишу свою бизнес-логику и мне уже совершенно не важно, что там было изначально: пара-тройка event’ов ли (как в случае с перетаскиванием объекта из предыдущего постинга), IAsyncResult ли, что-то ещё или всё это скомбинированное вместе…