简体   繁体   English

HttpClient的异步任务管理

[英]Async Task management for HttpClient

I'm creating a generic loader and I want to kick off an HttpClient SendAsync request. 我正在创建一个通用加载程序,我想启动一个HttpClient SendAsync请求。 However, some of these requests may take time, so I want to add the option to cancel, and notify upon completion. 但是,其中一些请求可能需要一些时间,因此我想添加取消选项,并在完成时通知。

This seems like a standard scenario imho. 这似乎是标准方案,恕我直言。

I'm not sure if this is the correct way to go about this, but based on some examples I've looked at, here is where I'm at. 我不确定这是否是解决问题的正确方法,但是根据我看过的一些示例,这就是我要去的地方。 If you look at the bottom of the code, my question is - at that point, do I check the response and raise a success or error event ? 如果您看一下代码的底部,我的问题是-那时, 我是否检查响应并引发成功或错误事件

    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?
    }

When you're looking at exposing task-related state like IsFetching , it's often cleaner and easier to just expose the Task itself. 当您要公开与任务相关的状态(例如IsFetching ,仅公开Task本身通常更清洁,更容易。

Something like this: 像这样:

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;
}

I would recommend throwing InvalidOperationException s for the StartFetch and CancelFetch operations if the IsFetching state is invalid. 我建议抛出InvalidOperationException S为StartFetchCancelFetch操作如果IsFetching状态无效。 This may seem annoying but it lets you catch programmer error and threading issues before they get to be a bigger, hidden problem. 这看起来很烦人,但是它可以让您在程序员成为一个更大的隐藏问题之前捕获程序员的错误和线程问题。

As for your asynchronous approach, your method should return a result. 至于异步方法,您的方法应返回结果。 So maybe something like private async Task<MyHttpResult> StartTask(...) . 所以也许像private async Task<MyHttpResult> StartTask(...) Your result should contain a way determine success, failure, and cancellation. 您的结果应包含确定成功,失败和取消的方法。

For example: 例如:

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;
  }
}

Many async methods will throw a TaskCanceledException if they are cancelled, so you can catch that to signify, like so: 如果许多异步方法被取消,它们将抛出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;
}

This is all assuming that your observers are also the callers, so they can await this method. 所有这些都假设您的观察者也是调用者,因此他们可以await此方法。 If this is not the case, your idea of an EventHandler makes sense. 如果不是这种情况,那么您对EventHandler的想法是有道理的。 Instead of returning the result, you could create a custom EventArgs class like so: 除了返回结果,您还可以创建一个自定义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;
      }
}

Then it's simply a matter of exposing a TaskResultEventHandler<HttpResponse> and your observers subscribing to it. 然后,只需暴露TaskResultEventHandler<HttpResponse>并让您的观察者订阅即可。 You could invoke it like so: 您可以这样调用它:

var handler = this.HttpTaskCompleted;

if(handler != null)
  handler(this, new TaskResultEventArgs<HttpResponse>(response, lastError, wasCancelled));

After you awaited the http call 等待http通话后

var response = await HttpClient.SendAsync(message, cancellationToken); 

You should test for cancellation: 您应该测试取消:

if(cancellationToken.IsCancellationRequested)
   //... do what you want, throw or return false or null, depending on how you want to handle this cancellation.

Or you can check and throw the Microsoft exception in one call : 或者,您可以在一次调用中检查并抛出Microsoft异常:

cancel.ThrowIfCancellationRequested();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM