I'm learning how to use Tasks in MVC and I get completly lost. I need to download the source from selected webpage, find one element, get its value and return it. That's it. For this I am using HtmlAgilityPack and HttpClient to fetch the webpage. The problem that occurs is where nothing is waiting for response from httpClient and thus results that response generation completed when Task was still in progress. (An asynchronous module or handler completed while an asynchronous operation was still pending.)
I read lots of threads in here ,codeproj and some blogs, still don't understand what's the problem. Most common explanation is about resulting type of void in async method, but I cannot find any other way to return awaiting value, than this:
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);
}
In order to figure out what's wrong you need to understand one key concept with async-await. When an async method hits the first await
keyword, control is yielded back to the calling method. This means that when you do this:
loadPage(url);
The method will synchronously run until it hits:
var response = await http.GetByteArrayAsync(url);
Which will yields control back to ParseWebSite
, which will continue execution and will probably end before the async operation has actually completed.
What you need to do is make LoadPage
return a Task
and await
for it's completion:
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);
}
Side Notes:
LoadPage
should become LoadPageAsync
.本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句