将 WebClient 方法转换为 async / await
Posted
技术标签:
【中文标题】将 WebClient 方法转换为 async / await【英文标题】:Converting a WebClient method to async / await 【发布时间】:2012-10-25 19:24:34 【问题描述】:我有一些现有代码要移植到 Windows 8 WinRT。代码从 URL 获取数据,异步调用传递的委托:
private void RequestData(string uri, Action<string> action)
var client = new WebClient();
client.DownloadStringCompleted += (s,e) => action(e.Result);
client.DownloadStringAsync(new Uri(uri));
转换为 WinRT 需要使用 HttpClient
和异步方法。我已经阅读了一些关于 async / await 的教程,但有点困惑。如何更改上述方法,但保留方法签名以避免更改更多代码?
【问题讨论】:
【参考方案1】:private async void RequestData(string uri, Action<string> action)
var client = new WebClient();
string data = await client.DownloadStringTaskAsync(uri);
action(data);
见:http://msdn.microsoft.com/en-us/library/hh194294.aspx
【讨论】:
5 的唯一答案显示了整个方法并保持相同的签名,因此是唯一回答问题的答案。 :) 简短说明:除非您正在执行触发并忘记请求,否则将返回类型更改为 Task,否则无论您是否等待 RequestData,调用线程都会继续执行 最好使用私有异步任务而不是异步无效 注意:这里不能错过的重要一点是被调用的不同方法:DownloadString*TASK*Async
vs DownloadStringAsync
。因为需要保留向后兼容性,所以需要在类中添加一个新方法
@KeyBored 根据您的具体用例,您可以尝试其中一种方法here【参考方案2】:
如何更改上述方法,但保留方法签名以避免更改更多代码?
最好的答案是“你不知道”。如果你使用async
,那么就一直使用下去。
private async Task<string> RequestData(string uri)
using (var client = new HttpClient())
return await client.GetStringAsync(uri);
【讨论】:
方法 RequestData 上缺少异步修饰符? :) FWIW,就保持“传递回调”签名而言,Cole 的回答似乎是合理的恕我直言 - 它仍然是异步的,使用等待,并调用“正确”线程上的操作(假设同步上下文到位)AFAICT?我会坦率地承认,像这样传递回调不如具有“正常”异步/等待代码的“看起来同步”风格那么好,但是如果您有现有的代码接受回调,那么仍然接受它们似乎是合理的?当然,“异步无效”通常是一个坏主意,所以也许我在这里忘记了一个问题案例。 :) 谢谢斯蒂芬,但我正在迁移代码,所以最小化更改是我的首要任务。这就是我想保留给定签名的原因。async void
存在一些问题。单元测试,其中之一。但最适用于这种情况的是它如何处理错误:HTTP 下载的任何问题都会直接在SynchronizationContext
上引发异常,而async Task
允许更自然地处理异常(从await
引发)。
【参考方案3】:
在this example 之后,您首先创建异步任务,然后使用await
获取其结果:
Task<string> downloadStringTask = client.DownloadStringTaskAsync(new Uri(uri));
string result = await downloadStringTask;
【讨论】:
【参考方案4】:var client = new WebClient();
string page = await client.DownloadStringTaskAsync("url");
或
var client = new HttpClient();
string page = await client.GetStringAsync("url");
【讨论】:
【参考方案5】:await
HttpClient.GetStringAsync Method 的结果:
var client = new HttpClient();
action(await client.GetStringAsync(uri));
【讨论】:
【参考方案6】:而这段代码是用于 UploadValuesAsync 的:
public class WebClientAdvanced : WebClient
public async Task<byte[]> UploadValuesAsync(string address, string method, IDictionary<string, string> data)
var nvc = new NameValueCollection();
foreach (var x in data) nvc.Add(x.Key, x.Value.ToStr());
var tcs = new TaskCompletionSource<byte[]>();
UploadValuesCompleted += (s, e) =>
if (e.Cancelled) tcs.SetCanceled();
else if (e.Error != null) tcs.SetException(e.Error);
else tcs.SetResult(e.Result);
;
UploadValuesAsync(new Uri(address), method, nvc);
var result = await tcs.Task;
return result;
【讨论】:
以上是关于将 WebClient 方法转换为 async / await的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Spring WebClient 响应转换为 ResponseEntity?
我应该检查WebClient.UploadFile的响应以了解上传是否成功?