0

hope somebody can help me with my problem.

I am trying to get the html code of a web site using the following code:

        public string DownloadString(string add)
        {
            string html = "";            
            using (WebClient client = new WebClient())
            {
                client.Proxy = null;
                client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
                while (html == "")
                {
                    try
                    {
                        html = client.DownloadString(add);

                    }
                    catch (WebException e)
                    {
                        html = "";
                    }
                }
                client.Dispose();
            }
            return html;
        }

And I need the string in this function(Caller):

        public HtmlNode get_html(string add)
        {
            add_val(add);
            Uri madd = new Uri(add);
            Stopwatch timer = Stopwatch.StartNew();
            Task<string> task = Task.Factory.StartNew<string>
        (() => DownloadString(add));
            string html = task.Result;
            //string html = DownloadString(add);
            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            //doc.Load(new StringReader(html));
            doc.LoadHtml(html);
            HtmlNode root = doc.DocumentNode; 
            timer.Stop();
            TimeSpan timespan = timer.Elapsed;
            label18.Text = String.Format("{0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10);            
            return root;
        }

I have tried html = await client.DownloadStringTaskAsync(new Uri(add)); but it doesn't seem to work, it still freezes the UI when i download a string.

Thank you in advance!

NoobCS
  • 43
  • 7

2 Answers2

1

In order to prevent blocking the UI, your code needs to be asynchronous all the way.

This code in particular is blocking the UI thread until the download completes:

Task<string> task = Task.Factory.StartNew<string>(() => DownloadString(add));
string html = task.Result;

What you need is to use await instead:

Task<string> task = Task.Run(() => DownloadString(add));
string html = await task;

This means your get_html method must be async:

public async Task<HtmlNode> get_htmlAsync(string add)

And all of its callers must use await, and become async, etc. You have to allow the asynchrony to grow all the way through the caller tree.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
1

The issue here is that call to task.Result is always blocking (if task is not ready, calling thread will wait for completion), so your UI thread will be blocked, waiting for task result. What you need to do, if you are using .Net Framework 4 (@Stephen wrote a solution for 4.5) is to use continuation in a following way.

public void get_html(string add)
        {
            add_val(add);
            Uri madd = new Uri(add);
            Stopwatch timer = Stopwatch.StartNew();
            Task.Factory.StartNew<string>(() => DownloadString(add))
                .ContinueWith(t => {
                    string html = task.Result;
                    HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                    doc.LoadHtml(html);
                    HtmlNode root = doc.DocumentNode; 
                    timer.Stop();
                    TimeSpan timespan = timer.Elapsed;
                    label18.Text = String.Format("{0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10);            
                    //
                    Update UI with results here
                    //
                }, TaskScheduler.FromCurrentSynchronizationContext());
        }

Alternatively you can set get_html return type to Task<string> or Task<HtmlNode> in order to use continuation on it.

nikis
  • 11,166
  • 2
  • 35
  • 45