it-roy-ru.com

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

Я создал проект Windows Phone 8.1 и пытаюсь запустить асинхронный метод GetResponse (строковый 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;
}

Он будет зависать от задачи. Wait ().

Я изменил свой метод нажатия кнопки на асинхронный и использовал await перед асинхронным методом, и я получил результат (await GetResponse<string>("url")). Что не так с Task<List<string>> task = GetResponse<string>("url")? Что я делаю неправильно?

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

36
Doniyor Niyozov

Ты жертва классического тупика. 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» для методов, которые возвращают Task или асинхронный, чтобы следовать соглашению 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);
}
64
Sriram Sakthivel

Вот что тебя убивает:

task.Wait();

Это блокирует поток пользовательского интерфейса до тех пор, пока задача не будет завершена, но задача - это асинхронный метод, который попытается вернуться к потоку пользовательского интерфейса после того, как он «приостановится» и ожидает асинхронного результата. Это не может сделать это, потому что вы блокируете поток пользовательского интерфейса ...

В вашем коде нет ничего, что действительно похоже на то, что оно должно быть в потоке пользовательского интерфейса, но при условии, что вы действительно делаете хотите этого, вы должны использовать:

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 для ясности.

24
Jon Skeet

используйте код ниже

 Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));
0
Mahesh