2

我正在学习如何使用MVC中的任务,我完全失去了。我需要从选定的网页下载源代码,找到一个元素,获取其值并返回它。而已。 为此,我使用HtmlAgilityPack和HttpClient来获取网页。 发生的问题是没有任何内容等待httpClient的响应,从而导致响应生成在Task仍在进行时完成。 (异步模块或处理程序完成异步操作仍未完成ASP MVC4等待与httpclient

我读了很多线程在这里,codeproj和一些博客,仍然不明白是什么问题。最常见的解释是关于导致异步方法无效的类型,但我无法找到任何其他方式返回等待值,超过此:

public float ReadPrice(Uri url) 
{ 
    switch (url.Host) 
    { 
     case "www.host1.xy": 
      return ParseXYZAsync(url).Result; 
     default: 
      return float.Parse("99999,99"); 
    } 
} 

private Task<float> ParseXYZAsync(Uri url) 
{ 
    loadPage(url); 

    var priceNode = document.DocumentNode.SelectSingleNode(
     @"//*[@id='pageWrapper']/div[4]/section[1]/div[4]/div[1]/div[1]/span"); 
    var price = priceNode.InnerText; 
    ... 
    return priceInFloat; 
} 

private async Task LoadPage(Uri url) 
{ 
    HttpClient http = new HttpClient(); 
    var response = await http.GetByteArrayAsync(url); 

    String source = Encoding.GetEncoding("utf-8") 
          .GetString(response, 0, response.Length - 1); 
    source = WebUtility.HtmlDecode(source); 

    document.LoadHtml(source); 
} 

回答

4

为了弄清楚什么是错的,你需要了解的一个关键概念异步等待。当一个异步方法碰到第一个关键字时,控制返回到调用方法。这意味着,当你这样做:

loadPage(url); 

的方法将同步运行,直到点击:

var response = await http.GetByteArrayAsync(url); 

这将产量控制回ParseWebSite,这将继续执行和异步之前可能会结束操作已经完成。

你需要做的是使LoadPage回报Taskawait为它的完成:

private async Task<float> ParseWebsiteAsync(Uri url) 
{ 
    await LoadPageAsync(url); 

    var priceNode = document.DocumentNode.SelectSingleNode 
     (@"//*[@id='pageWrapper']/div[4]/section[1]/div[4]/div[1]/div[1]/span"); 
    var price = priceNode.InnerText; 
    return priceInFloat; 
} 

private async Task LoadPageAsync(Uri url) 
{ 
    HttpClient http = new HttpClient(); 
    var source = await http.GetAsStringAsync(url); 

    source = WebUtility.HtmlDecode(source); 
    document.LoadHtml(source); 
} 

旁注:

  1. 做好后续.NET命名约定。方法应该是PascalCase而不是驼峰
  2. 请为异步方法添加“异步”后缀。例如,LoadPage应该变成LoadPageAsync
+0

谢谢你给出了一个惊人的答案,它解释了很多。这是否意味着只需要获取一个网页,我将需要两项任务?这就是为什么操作系统中有这么多人,当简单的事情需要超过一个。 – Vilo

+1

@Vilo您需要的任务数量与您要创建的方法调用数量相同,因为异步会从调用堆栈的顶部到底部“一路”。 –

+0

我发现另一个问题,当我将返回类型更改为任务时,标准同步流的某些内容正在调用此ParseWebsiteAsync()方法。它表示无法将任务转换为浮动。我需要这种方法(其中包含一个开关())是同步的。将异步添加到此方法将产生需要向上一个和另一个添加异步,直到我点击控制器将继续创建许多不同的任务。这个puropse有什么好的解决方案?我需要ParseWebsiteAsync返回一个浮点数(或父方法)。 – Vilo

1

私人异步无效loadPage(URI URL)需要返回一个任务:

private async Task loadPage(Uri url) 

,那么你需要等待它的调用方法:

private async Task<float> parseWEBSITEXY(Uri url) 
{ 
await loadPage(url); 
... 
} 

在你的代码加载页面开始并立即返回。 还有一件事 - 不推荐使用异步无效,除了事件处理程序。始终返回任务。