如何保持重试反应方法直到成功?

问题描述:

这是我的异步下载无效的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; 
} 
+0

使用goto它可以做的工作 – Burimi

你的方法产生一个热的observable,这意味着它返回时已经开始加载,并且每个新的订阅都不会向web服务器创建一个新的请求。你需要用另一种方法,你并使用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

一个更直接的方法是只使用Observable.Defer() –

+0

oops,我已经使自己延迟实现,但我没有意识到它,因为我只得到一个输出在重试(3)后进行控制台。我的错。 –

如果它是一个异步函数。重复检查意味着您将其转换为同步功能调用。这是你真正想做的事吗?

你可以有一个专用的线程调用这个异步函数并在调用此函数后自行阻塞。当创建这个线程时,将它传递给应该在异步函数返回后调用的委托。完成后,使用错误代码调用委托。

希望这回答你的问题。

我想你至少有一个体面的“这里是一些代码”的答案,所以我将专注于更广泛的手拿着。

我想看的第一件事是design guidelines for Rx。它是一个简短的(34页)PDF文档,可帮助将范式从“订阅”更改为推送,或从IEnumerable更改为IObservable。

如果您想进一步了解,有.NETJavaScript的PDF HOL(使用实验室)。您可以在Rx页面找到其他资源(start here)。