Ответ заключается не в том, чтобы заставить пользовательский интерфейс вообще ждать, а в том, чтобы «идти с (асинхронным) потоком».
В конечном счете, асинхронная работа в C # 5 решит эту конкретную проблему,но пока нет времени для его выпуска для настольной CLR, не говоря уже о Silverlight или WP7.
Я лично рекомендовал бы изучить Microsoft.Phone.Reactive
, которая является версией Reactive Extensions (Rx) для WP7 и поставляется сSDK.Это довольно большой вопрос, который требует достаточно много времени, чтобы разобраться, но он действительно может упростить вашу работу с асинхронными сценариями.
Предполагая, что каждая из ваших веб-служб возвращает разные типы данных, я бы:
- Оберните каждый вызов веб-службы в IObservable 1
- Используйте
Do
для «просмотра» сообщения и выполнения побочных эффектов (например, назначениязначение локально) - Используйте
Select
, чтобы "нормализовать" типы, чтобы они все были одного типа (требуется для следующего шага) - Используйте
ForkJoin
для выполнения каждого изпараллельные запросы и их обработка после завершения каждого
1 Создание IObservable для вашего запроса действительно зависит от того, какой асинхронный шаблон вы используете.Предполагая, что вы используете WebClient
, вот метод расширения, который создает Observable из DownloadStringAsync
в качестве образца (он может выглядеть сложным, но он просто обрабатывает ошибки и отмены):
public static class ObservableWebClient
{
public static IObservable<string> DownloadStringObservable(
this WebClient webClient, Uri uri)
{
return Observable.Create(observer =>
{
var disposable = new CompositeDisposable();
var completedObservable = Observable.FromEvent<
DownloadStringCompletedEventHandler,
DownloadStringCompletedEventArgs
>(
h => new DownloadStringCompletedEventHandler(h),
h => webClient.DownloadStringCompleted += h,
h => webClient.DownloadStringCompleted h= h
);
disposable.Add(completedObservable
.SelectMany(ev =>
{
return (ev.EventArgs.Error != null)
? Observable.Throw<string>(ev.EventArgs.Error)
: Observable.Return(ev.EventArgs.Result);
})
.Subscribe(observer));
disposable.Add(Disposable.Create(
() => webClient.CancelAsync()));
return disposable;
});
}
}
Затем вы можетеиспользуйте это так:
Обратите внимание, что я пропустил Do
+ "нормализующие" шаги, потому что все мои типы данных одинаковы (String).Таким образом, я могу подписаться на них все в виде массива (это тонкость работы ForkJoin
, если вам интересно)
var webClientA = new WebClient();
var webClientB = new WebClient();
var webClientC = new WebClient();
Observable.ForkJoin(
webClientA.DownloadStringObservable(uriA),
webClientB.DownloadStringObservable(uriB),
webClientC.DownloadStringObservable(uriC),
)
.ObserveOnDispatcher()
.Subscribe(dataArray =>
{
// All three have completed
this.DataA = dataArray[0];
this.DataB = dataArray[1];
this.DataC = dataArray[2];
});