将 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】:

awaitHttpClient.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?

将 Func 委托与 Async 方法一起使用

我应该检查WebClient.UploadFile的响应以了解上传是否成功?

WebClient 的 bodyToMono 关于空体预期行为

如何将 onload 承诺转换为 Async/Await

Reactive Spring WebClient - 进行 SOAP 调用