如果收到新请求,如何取消以前的任务?

How to cancel previous Task if new request recieved?

我会尽量简化这里的情况,使之更加简洁明了。所以,我正在开发一个 WinRT 应用程序,用户在 TextBox 中输入文本,并在 2 秒后在其 TextChanged 事件中输入文本,我需要制作一个远程请求根据用户文本获取数据。

现在用户输入文本并且 Web 请求已初始化,但用户立即写入另一个术语。所以,我需要取消第一个网络请求并触发新的请求。

考虑以下作为我的代码:

private CancellationTokenSource cts;

public HomePageViewModel()
{
    cts = new CancellationTokenSource();
}

private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)
{
    //Cancel previous request before making new one        

    //GetMembers() is using simple HttpClient to PostAsync() and get response
    var members = await _myService.GetMembers(someId, cts.Token);

    //other stuff based on members
}

我知道 CancellationToken 在这里发挥了作用,但我不知道是如何发挥作用的。

我会像这样实现 GetMembers 方法:

private async Task<List<Member>> GetMembers(int id, CancellationToken token)
    {
        try
        {
            token.ThrowIfCancellationRequested();
            HttpResponseMessage response = null;

            using (HttpClient client = new HttpClient())
            {
                response = await client.PostAsync(new Uri("http://apiendpoint"), content)
                                       .AsTask(token);
            }

            token.ThrowIfCancellationRequested();
            // Parse response and return result
        }
        catch (OperationCanceledException ocex)
        {
            return null;
        }
    }

剩下的只是调用 cts.Cancel() 方法并在处理程序中每次调用 GetMembers 之前创建一个新的 CancellationTokenSource 实例。当然,正如@Russell Hickey 提到的,cts 应该是全局的。 (如果这个 class 有多个实例并且你总是想在调用这个处理程序时取消 GetMembers 方法,甚至是静态的。通常我还有一个 class 来包装结果和有一个额外的 属性 IsSuccessful 来区分真正的 null 结果和失败的操作。

你已经差不多明白了。核心思想是单个CancellationTokenSource只能取消一次,所以每次操作都要新建一个

private CancellationTokenSource cts;

private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)
{
  // If there's a previous request, cancel it.
  if (cts != null)
    cts.Cancel();

  // Create a CTS for this request.
  cts = new CancellationTokenSource();

  try
  {
    var members = await _myService.GetMembers(someId, cts.Token);

    //other stuff based on members
  }
  catch (OperationCanceledException)
  {
    // This happens if this operation was cancelled.
  }
}