Async, await и Task<T> на примере загрузки страниц

AlexanderWM
Дата: 02.07.2014 22:26:58
Есть у меня обычный метод, для получения содержимого страниц GET методов. При необходимости - с использованием кук

public static string Get(string url, bool clearCookie, ref CookieContainer container)
        {
            string output = null;
            try
            {
                HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(url);
                if (clearCookie) WebReq.CookieContainer = new CookieContainer();
                else WebReq.CookieContainer = container;
                WebReq.Method = "GET";
                WebReq.AllowAutoRedirect = true;
                WebReq.ContentType = "application/x-www-form-urlencoded";

                HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse();
                Stream Answer = WebResp.GetResponseStream();
                container = WebReq.CookieContainer;
                StreamReader _Answer = new StreamReader(Answer, Encoding.GetEncoding(1251));
                output = _Answer.ReadToEnd();
                return output.Trim() + "\n";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }


Пока всё было линейно - проблем не было. Сейчас встала задача получать содержимое страниц асинхронно. Вот и нужно переписать этот метод с помощью модных async/await

И сразу возникли вопросы. Асинхронные методы - либо void, либо возвращают Task<T>. В моём случае логично написать
public async Task<string> Get (string url, bool clearCookie, ref CookieContainer container)
...
а в коде позаменять GetResponse() на GetResponseAsync().


Что дальше - непонятно. Совсем. В результате я получу Task<string>. Как из этого результата "выдрать" нужные мне string? Как вообще обращаться с этим методом? Помогите, пожалуйста, с методом для асинхронной загрузки страницы.
cdtyjv
Дата: 02.07.2014 22:48:52
AlexanderWM,
В смысле "как работать с Task"? Ну вы API его смотрели? Когда вам понадобится результат, вызываете на нем Result, который дождется окончания работы и вернет вам стрингу. Но это немного неверный подход, так как вы все равно где-то будете ждать его завершения.
Вместо этого вам надо внутри вашего метода вызвать await на какой-нибудь асинхронной операции чтения URL, потом вызвать await на ее результате, а потом написать код обработки результата. Вот и все. Таким образом, когда поток, который реально вытаскивает данные с веба, завершит выполнение, он подхватит то, что вы написали после await, и выполнит это.
Но радикально ничего не поменяется у вас, это просто синтаксический сахар вокруг обычного Task (хоть и работает он несколько иначе с точки зрения скомпилированного кода), так что не ожидайте чего-то особого.
AlexanderWM
Дата: 02.07.2014 23:04:07
cdtyjv
AlexanderWM,
В смысле "как работать с Task"? Ну вы API его смотрели? Когда вам понадобится результат, вызываете на нем Result, который дождется окончания работы и вернет вам стрингу. Но это немного неверный подход, так как вы все равно где-то будете ждать его завершения.
Вместо этого вам надо внутри вашего метода вызвать await на какой-нибудь асинхронной операции чтения URL, потом вызвать await на ее результате, а потом написать код обработки результата. Вот и все. Таким образом, когда поток, который реально вытаскивает данные с веба, завершит выполнение, он подхватит то, что вы написали после await, и выполнит это.
Но радикально ничего не поменяется у вас, это просто синтаксический сахар вокруг обычного Task (хоть и работает он несколько иначе с точки зрения скомпилированного кода), так что не ожидайте чего-то особого.

Вы бы не могли для наглядности создать небольшую демонстрацию?
cdtyjv
Дата: 02.07.2014 23:11:04
Где-то в степи
Дата: 03.07.2014 00:31:10
AlexanderWM,
может проще сделать, нативно - народными средствами?
+
  class Program
    {
        static Func<string, bool, CookieContainer, string> func;
        static void Main(string[] args)
        {
            func = Get;
            func.BeginInvoke("http://www.sql.ru/forum/1103286/async-await-i-task-t-na-primere-zagruzki-strani", true, new CookieContainer(), MyWorker, null);
            Console.Read();
        }

        static void MyWorker(IAsyncResult result)
        {
           var str= func.EndInvoke(result);
            Console.WriteLine(str);
        }

        public static string Get(string url, bool clearCookie,  CookieContainer container)
        {
            string output = null;
            try
            {
                HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(url);
                if (clearCookie) WebReq.CookieContainer = new CookieContainer();
                else WebReq.CookieContainer = container;
                WebReq.Method = "GET";
                WebReq.AllowAutoRedirect = true;
                WebReq.ContentType = "application/x-www-form-urlencoded";

                HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse();
                Stream Answer = WebResp.GetResponseStream();
                container = WebReq.CookieContainer;
                StreamReader _Answer = new StreamReader(Answer, Encoding.GetEncoding(1251));
                output = _Answer.ReadToEnd();
                return output.Trim() + "\n";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }


что то меня смущает отлов исключения без выброса выше, нарушен принцип самурая..
AlexanderWM
Дата: 03.07.2014 01:17:25
Спасибо! Вполне интересное решение. Отдельное спасибо, за то, что заставили погуглить про принцип самурая :)
john2007
Дата: 03.07.2014 02:34:40
В принципе все сказали.

Можно в одном методе при желании.


class Program
{
public delegate string GetDelegate (string s, bool b, CookieContainer cc);

static void Main(string[] args)
{
GetDalegate func;
var ar = func.BeginInvoke("http://www.sql.ru/forum/1103286/async-await-i-task-t-na-primere-zagruzki-strani", true, new CookieContainer(), null, null);

// делаем, что хотим

// можем при желании проверить
// if (ar.IsCompleted) { }

var s = func.EndIvoke(ar) // получили результат

public string GetDelegate (string s, bool b, CookieContainer cc)
{
...........
}
}