[英]Async Task management for HttpClient
我正在创建一个通用加载程序,我想启动一个HttpClient SendAsync
请求。 但是,其中一些请求可能需要一些时间,因此我想添加取消选项,并在完成时通知。
这似乎是标准方案,恕我直言。
我不确定这是否是解决问题的正确方法,但是根据我看过的一些示例,这就是我要去的地方。 如果您看一下代码的底部,我的问题是-那时, 我是否检查响应并引发成功或错误事件 ?
public bool StartFetch()
{
if (IsFetching) return false;
IsFetching = true;
mCancellationTokenSource = new CancellationTokenSource();
// this is not awaited, so execution should continue
StartTask(request, mCancellationTokenSource.Token);
return true;
}
public bool CancelFetch()
{
// send cancellation
if (mCancellationTokenSource != null)
mCancellationTokenSource.Cancel();
Cleanup();
return true;
}
private async Task StartTask(LFHttpRequest request, CancellationToken cancellationToken)
{
var message = new HttpRequestMessage(request.Method, request.Uri);
var response = await HttpClient.SendAsync(message, cancellationToken);
// at this point, do I take a look at response and raise a custom OnSuccess or OnError event???
// or do I want to grab the task from `SendAsync`, check for completed or faulted?
}
当您要公开与任务相关的状态(例如IsFetching
,仅公开Task
本身通常更清洁,更容易。
像这样:
public Task<T> FetchTask { get; private set; }
public bool StartFetch()
{
if (FetchTask != null) return false;
mCancellationTokenSource = new CancellationTokenSource();
FetchTask = FetchAsync(request, mCancellationTokenSource.Token);
return true;
}
public bool CancelFetch()
{
// send cancellation
if (mCancellationTokenSource != null)
mCancellationTokenSource.Cancel();
FetchTask = null;
return true;
}
private async Task<T> FetchAsync(LFHttpRequest request, CancellationToken cancellationToken)
{
var message = new HttpRequestMessage(request.Method, request.Uri);
var response = await HttpClient.SendAsync(message, cancellationToken);
response.EnsureSuccessStatusCode();
var ret = // Convert response.Content into T.
return ret;
}
我建议抛出InvalidOperationException
S为StartFetch
和CancelFetch
操作如果IsFetching
状态无效。 这看起来很烦人,但是它可以让您在程序员成为一个更大的隐藏问题之前捕获程序员的错误和线程问题。
至于异步方法,您的方法应返回结果。 所以也许像private async Task<MyHttpResult> StartTask(...)
。 您的结果应包含确定成功,失败和取消的方法。
例如:
public sealed class MyHttpResult
{
public HttpResponse Result { get; private set; }
public Exception Error { get; private set; }
public bool WasCancelled { get; private set; }
public MyHttpResult(HttpResponse result, Exception error, bool wasCancelled)
{
this.Result = result;
this.Error = error;
this.WasCancelled = wasCancelled;
}
}
如果许多异步方法被取消,它们将抛出TaskCanceledException
,因此您可以catch
它来表示,例如:
async Task<MyHttpResult> StartTask(LFHttpRequest request, CancellationToken cancellationToken)
{
var message = new HttpRequestMessage(new HttpMethod(request.Method), request.Uri);
HttpResponse response = null;
Exception lastError = null;
bool wasCancelled = false;
try
{
response = await MessageInvoker.SendAsync(message, cancellationToken);
}
catch(TaskCanceledException)
{
wasCancelled = true;
}
catch(Exception ex)
{
lastError = ex;
}
var result = new MyHttpResult(response, lastError, wasCancelled);
return result;
}
所有这些都假设您的观察者也是调用者,因此他们可以await
此方法。 如果不是这种情况,那么您对EventHandler
的想法是有道理的。 除了返回结果,您还可以创建一个自定义EventArgs
类,如下所示:
public delegate void TaskResultEventHandler<T>(object sender, TaskResultEventArgs<T> e);
public sealed class TaskResultEventArgs<T> : EventArgs
{
public T Result { get; private set; }
public Exception Error { get; private set; }
public bool WasCancelled { get; private set; }
public TaskResultEventArgs(T result, Exception error, bool wasCancelled)
{
this.Result = result;
this.Error = error;
this.WasCancelled = wasCancelled;
}
}
然后,只需暴露TaskResultEventHandler<HttpResponse>
并让您的观察者订阅即可。 您可以这样调用它:
var handler = this.HttpTaskCompleted;
if(handler != null)
handler(this, new TaskResultEventArgs<HttpResponse>(response, lastError, wasCancelled));
等待http通话后
var response = await HttpClient.SendAsync(message, cancellationToken);
您应该测试取消:
if(cancellationToken.IsCancellationRequested)
//... do what you want, throw or return false or null, depending on how you want to handle this cancellation.
或者,您可以在一次调用中检查并抛出Microsoft异常:
cancel.ThrowIfCancellationRequested();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.