[英]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.