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

在你的代碼加載頁面開始並立即返回。 還有一件事 - 不推薦使用異步無效,除了事件處理程序。始終返回任務。