Как продолжать повторять реактивный метод, пока он не добьется успеха? - PullRequest
0 голосов
/ 17 октября 2011

Вот мое реактивное расширение для асинхронной загрузки для WebClient. Каков наилучший способ вызывать «DownloadStringAsync» снова и снова, пока операция не будет выполнена успешно?

Примерно так, но реагирующим образом:

while (true)
{
  var result = DownloadStringAsync();
  if (result)
  {
    return;
  }
}

МОЙ КОД:

[Serializable]
public class WebClientException : Exception
{
    public WebClientResponse Response { get; set; }

    public WebClientException()
    {
    }

    public WebClientException(string message)
        : base(message)
    {
    }

    public WebClientException(string message, Exception innerException)
        : base(message, innerException)
    {
    }

    protected WebClientException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }
}

public class WebClientResponse
{
    public WebHeaderCollection Headers { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public string Result { get; set; }
    public WebException Exception { get; set; }
}

public static IObservable<WebClientResponse> DownloadStringAsync(this WebClient webClient, Uri address, WebHeaderCollection requestHeaders)
{
    var asyncResult =
        Observable.FromEventPattern<DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs>
            (ev => webClient.DownloadStringCompleted += ev, ev => webClient.DownloadStringCompleted -= ev)
            .ObserveOn(Scheduler.TaskPool)
            .Select(o =>
                        {
                            var ex = o.EventArgs.Error as WebException;

                            if (ex == null)
                            {
                                var wc = (WebClient) o.Sender;

                                return new WebClientResponse {Headers = wc.ResponseHeaders, Result = o.EventArgs.Result};
                            }

                            var wcr = new WebClientResponse {Exception = ex};

                            var r = ex.Response as HttpWebResponse;
                            if (r != null)
                            {
                                wcr.Headers = r.Headers;
                                wcr.StatusCode = r.StatusCode;

                                var s = r.GetResponseStream();
                                if (s != null)
                                {
                                    using (TextReader tr = new StreamReader(s))
                                    {
                                        wcr.Result = tr.ReadToEnd();
                                    }
                                }
                            }

                            throw new WebClientException {Response = wcr};
                        })
            .Take(1);

    if (requestHeaders != null)
    {
        foreach (var key in requestHeaders.AllKeys)
        {
            webClient.Headers.Add(key, requestHeaders[key]);
        }
    }

    webClient.DownloadStringAsync(address);

    return asyncResult;
}

Ответы [ 3 ]

1 голос
/ 17 октября 2011

Ваш метод создает горячую наблюдаемую, что означает, что он уже начал загружаться, когда он возвращается, и каждая новая подписка не создает новый запрос к веб-серверу. Вам нужно обернуть ваш метод в другой и использовать Observable.Create (для создания холодной наблюдаемой, которая создает новый запрос при каждой подписке):

public static IObservable<WebClientResponse> DownloadStringAsync(this WebClient webClient, Uri address, WebHeaderCollection requestHeaders)
{
    return Observable
        .Create(observer => 
        {
            DownloadStringAsyncImpl(webClient, address, requestHeaders)
                .Subscribe(observer);
            return () => { webClient.CancelAsync(); };
        });
}

Здесь DownloadStringAsyncImpl - ваша предыдущая реализация DownloadStringAsync, в то время как публичный метод был заменен.

Теперь вы можете повторить асинхронный метод, пока он не выполнится следующим образом:

myWebClient
    .DownloadStringAsync( /* args... */)
    .Retry()
    .Subscribe(result => { 
         /* now I've got a result! */
    });
1 голос
/ 17 октября 2011

Я думаю, что у вас есть хотя бы один приличный ответ "здесь какой-то код", поэтому я остановлюсь на более общем удерживании руки.для Rx .Это короткий (34 стр.) PDF-документ, который помогает изменить парадигму от «подписок» к push-уведомлениям или перехода от IEnumerable к IObservable.

Если вы хотите пойти немного дальше, есть PDF HOL (практические занятия) для .NET и JavaScript .Вы можете найти другие ресурсы на страницах Rx ( начать здесь ).

0 голосов
/ 17 октября 2011

Если это асинхронная функция.Выполнение повторной проверки означает, что вы превратили его в вызов функции синхронизации.Это то, что вы действительно хотите сделать?

Вы можете создать выделенный поток, вызывающий эту асинхронную функцию, и заблокировать себя после вызова этой функции.При создании этого потока передайте ему делегат, который должен быть вызван после возврата асинхронной функции.По завершении позвоните делегату с кодом ошибки.

Надеюсь, что это ответ на ваш вопрос.

...