单击按钮时调用异步方法

我创建了一个 Windows Phone 8.1项目,我试图在按钮上运行 async方法,然后等待方法完成,但是方法从来没有完成过。这是我的代码:

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之前的 async方法,我得到的结果(await GetResponse<string>("url"))。Task<List<string>> task = GetResponse<string>("url")怎么了? 我做错了什么?

谢谢你的帮助!

151299 次浏览

This is what's killing you:

task.Wait();

That's blocking the UI thread until the task has completed - but the task is an async method which is going to try to get back to the UI thread after it "pauses" and awaits an async result. It can't do that, because you're blocking the UI thread...

There's nothing in your code which really looks like it needs to be on the UI thread anyway, but assuming you really do want it there, you should use:

private async void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
var items = await task;
// Presumably use items here
}

Or just:

private async void Button_Click(object sender, RoutedEventArgs
{
var items = await GetResponse<MyObject>("my url");
// Presumably use items here
}

Now instead of blocking until the task has completed, the Button_Click method will return after scheduling a continuation to fire when the task has completed. (That's how async/await works, basically.)

Note that I would also rename GetResponse to GetResponseAsync for clarity.

You're the victim of the classic deadlock. task.Wait() or task.Result is a blocking call in UI thread which causes the deadlock.

Don't block in the UI thread. Never do it. Just await it.

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

Btw, why are you catching the WebException and throwing it back? It would be better if you simply don't catch it. Both are same.

Also I can see you're mixing the asynchronous code with synchronous code inside the GetResponse method. StreamReader.ReadToEnd is a blocking call --you should be using StreamReader.ReadToEndAsync.

Also use "Async" suffix to methods which returns a Task or asynchronous to follow the TAP("Task based Asynchronous Pattern") convention as Jon says.

Your method should look something like the following when you've addressed all the above concerns.

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

use below code

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

@ChrisWalsh: If you use Task.Run() and call the async task inside that function, the task will run on a new UI thread and prevent blocking your UI.