Подтвердить что ты не робот

Вызов метода асинхронизации при нажатии кнопки

Я создал проект 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")?  Что я делаю неправильно?

Спасибо за помощь!

4b9b3361

Ответ 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")));