wpf 单击按钮时调用异步方法

js5cn81o  于 2022-11-18  发布在  其他
关注(0)|答案(4)|浏览(300)

我创建了Windows Phone 8.1项目,并尝试在单击按钮时运行async方法GetResponse<T>(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,并在async方法之前使用了await,结果是(await GetResponse<string>("url"))。Task<List<string>> task = GetResponse<string>("url")有什么问题?我做错了什么?
谢谢你的帮助!

qhhrdooz

qhhrdooz1#

您是典型死锁的受害者。task.Wait()task.Result是UI线程中导致死锁的阻塞调用。
永远不要这样做,只是这样做。

private async void Button_Click(object sender, RoutedEventArgs 
{
      var task = GetResponseAsync<MyObject>("my url");
      var items = await task;
}

顺便说一句,你为什么要抓住WebException然后把它扔回去呢?如果你不抓住它就更好了。两者都是一样的。
我还可以看到您在GetResponse方法中混合了异步代码和同步代码。StreamReader.ReadToEnd是一个阻塞调用--您应该使用StreamReader.ReadToEndAsync
还可以对返回Task或asynchronous的方法使用“Async”后缀,以遵循TAP(“基于任务的异步模式”)约定,如Jon says
解决了上述所有问题后,您的方法应该如下所示。

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);
}
jaql4c8m

jaql4c8m2#

这就是要你命的东西

task.Wait();

这会阻塞UI线程,直到任务完成--但是任务是一个异步方法,它会在“暂停”并等待异步结果后尝试返回UI线程。它不能这样做,因为你正在阻塞UI线程...
代码中没有任何东西看起来确实需要放在UI线程上,但假设您确实 * 确实 * 希望它在那里,您应该用途:

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方法不再 * 阻塞 * 直到任务完成,而是在调度一个continuation之后返回,以便在任务完成时触发(这基本上就是async/await的工作原理)。
请注意,为了清楚起见,我还将GetResponse重命名为GetResponseAsync

bqf10yzr

bqf10yzr3#

@克里斯·沃尔什:如果您使用Task.Run()并在该函数中调用异步任务,则该任务将在新的UI线程上运行并防止阻塞您的UI。

zf9nrax1

zf9nrax14#

使用以下代码

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

相关问题