Вызов метода асинхронизации при нажатии кнопки
Я создал проект Windows Phone 8.1, и я пытаюсь запустить метод getResponse (string url) при нажатии кнопки и ждет завершения метода, но метод никогда не заканчивается. Вот мой код:
private void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
task.Wait();
var items = task.Result; //break point here
}
public static async Task<List<T>> GetResponse<T>(string url)
{
List<T> items = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
try
{
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = strReader.ReadToEnd();
items = JsonConvert.DeserializeObject<List<T>>(text);
}
catch (WebException)
{
throw;
}
return items;
}
Он будет висеть на task.Wait().
Я изменил метод нажатия кнопки на асинхронный вызов и использовал его до появления метода async, и получим результат (await GetResponse<string>("url")
). Что случилось с Task<List<string>> task = GetResponse<string>("url")
?
Что я делаю неправильно?
Спасибо за помощь!
Ответы
Ответ 1
Ты жертва классического тупика. task.Wait()
или task.Result
- это блокирующий вызов в потоке пользовательского интерфейса, который вызывает тупик.
Не блокировать в потоке пользовательского интерфейса. Никогда не делай этого. Просто подождите.
private async void Button_Click(object sender, RoutedEventArgs
{
var task = GetResponseAsync<MyObject>("my url");
var items = await task;
}
Кстати, почему вы ловите WebException
и бросаете его обратно? Было бы лучше, если бы вы просто не поймали его. Оба они одинаковы.
Также я вижу, что вы смешиваете асинхронный код с синхронным кодом внутри метода GetResponse
. StreamReader.ReadToEnd
- блокирующий вызов - вы должны использовать StreamReader.ReadToEndAsync
.
Также используйте суффикс "Async" для методов, которые возвращают задачу или асинхронно, чтобы следовать соглашению TAP ( "Асимметричный шаблон на основе задачи" ) как Джон говорит.
Ваш метод должен выглядеть примерно так, как только вы решите все вышеперечисленные проблемы.
public static async Task<List<T>> GetResponseAsync<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = await strReader.ReadToEndAsync();
return JsonConvert.DeserializeObject<List<T>>(text);
}
Ответ 2
Это то, что вас убило:
task.Wait();
Это блокирование потока пользовательского интерфейса до завершения задачи, но задача представляет собой метод async, который попытается вернуться к потоку пользовательского интерфейса после того, как он "приостанавливается" и ожидает результат async. Он не может этого сделать, потому что вы блокируете поток пользовательского интерфейса...
В вашем коде нет ничего похожего на то, что он должен быть в потоке пользовательского интерфейса, но если вы действительно хотите его там, вы должны использовать:
private async void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
var items = await task;
// Presumably use items here
}
Или просто:
private async void Button_Click(object sender, RoutedEventArgs
{
var items = await GetResponse<MyObject>("my url");
// Presumably use items here
}
Теперь вместо блокировки, пока задача не завершится, метод Button_Click
вернется после планирования продолжения запуска, когда задача завершится. (Это как работает async/await, в основном.)
Обратите внимание, что для ясности я также переименовал бы GetResponse
в GetResponseAsync
.
Ответ 3
используйте ниже код
Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));