【问题标题】:async task cancellation c# xamarinc# xamarin 异步任务取消
【发布时间】:2016-05-08 01:24:19
【问题描述】:

我有搜索用户的功能。我提供了一个 textview 并在该 textview 更改方法上我正在触发一种从 Web 服务器获取数据的方法。但是当用户输入字母时我遇到了问题,因为所有 api 命中都是在异步任务中完成的。等待 100 毫秒后应该触发服务,这意味着如果用户输入字母“a”然后在 100 毫秒内没有输入,那么我们必须点击服务。但是如果用户键入“a”然后“b”然后“c”,那么应该为“abc”点击一项服务,而不是全部。

我点击了官方链接,但它对我没有帮助 https://msdn.microsoft.com/en-us/library/jj155759.aspx

所以基本上这是我的代码

textview.TextChange+= (sender,e) =>{
  CancellationTokenSource cts = new CancellationTokenSource();
      await Task.Delay(500); 
      // here some where I have to pass cancel token
      var lst = await APIClient.Instance.GetUserSearch("/user/get?searchTerm=" + newText, "application/json",cts);
      if (lst != null && lst.Count > 0){ 
        lstSearch.AddRange(lst);
      }
  }

这是我获取用户的方法

 public async Task<JResponse> GetUserSearch<JResponse>(string uri, string contentType,CancellationToken cts)
    {
        try
        {
            Console.Error.WriteLine("{0}", RestServiceBaseAddress + uri);

            string url = string.Format("{0}{1}", RestServiceBaseAddress, uri); 

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.ContentType = contentType;
            if (Utility.CurrentUser != null && !string.IsNullOrWhiteSpace(Utility.CurrentUser.AuthToken))
            {
                request.Headers.Add("api_key", Utility.CurrentUser.AuthToken);
            }
            request.Method = "POST";

            var payload = body.ToString();

            request.ContentLength = payload.Length;

            byte[] byteArray = Encoding.UTF8.GetBytes(body.ToString());
            request.ContentLength = byteArray.Length;

            using (var stream = await request.GetRequestStreamAsync())
            {
                stream.Write(byteArray, 0, byteArray.Length);
                stream.Close();
            }
            using (var webResponse = await request.GetResponseAsync())
            {
                var response = (HttpWebResponse)webResponse;
                using (var reader1 = new StreamReader(response.GetResponseStream()))
                {
                    Console.WriteLine("Finished : {0}", uri);
                    var responseStr = reader1.ReadToEnd();  
                    var responseObj = JsonConvert.DeserializeObject<JResponse>(
                        responseStr,
                        new JsonSerializerSettings()
                        {
                            MissingMemberHandling = MissingMemberHandling.Ignore,
                            NullValueHandling = NullValueHandling.Ignore
                        });
                    return responseObj;
                }
            }
        }
        catch (System.Exception ex)
        {
            Utility.ExceptionHandler("APIClient", "ProcessRequestAsync", ex);
        }

        return default(JResponse);
    }

【问题讨论】:

标签: c# xamarin async-await


【解决方案1】:

在您的示例中,您正在创建一个CancellationTokenSource - 您需要持有对它的引用,以便下次调用处理程序时,可以取消先前的搜索。 Here 是一个示例控制台应用程序,您应该能够运行它,但重要的是在处理程序中。

private CancellationTokenSource _cts;

private async void TextChangedHandler(string text)   // async void only for event handlers
{
    try
    {
        _cts?.Cancel();     // cancel previous search
    }
    catch (ObjectDisposedException)     // in case previous search completed
    {
    }

    using (_cts = new CancellationTokenSource())
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token);  // buffer

            var users = await _userService.SearchUsersAsync(text, _cts.Token);
            Console.WriteLine($"Got users with IDs: {string.Join(", ", users)}");
        }
        catch (TaskCanceledException)       // if the operation is cancelled, do nothing
        {
        }
    }
}

请务必将CancellationToken 传递给async 方法的所有,包括那些执行网络请求的方法,这样您就可以将取消信号传递到最低级别。 p>

【讨论】:

  • 感谢正文,已过去 3 小时,并在 2 分钟内解决了您的帖子 :)
【解决方案2】:

尝试使用计时器。然后你第一次改变文本 - 你创建它。然后在此之后更改文本-重新启动计时器。如果您在 700 毫秒内不更改文本 - 计时器将触发 PerformeSearch 方法。使用 Timeout.Infinite 作为定时器周期参数以防止它重新启动。

textview.TextChange += (sender,e) => 
{
   if (_fieldChangeTimer == null)
     _fieldChangeTimer = new Timer(delegate
     {
                PerformeSearch();
     }, null, 700, Timeout.Infinite);
   else
   {
     _fieldChangeTimer.Change(700, Timeout.Infinite);
   }
};

【讨论】:

    【解决方案3】:

    实例化 CancellationTokenSource。

    cts = new CancellationTokenSource();示例方法

    private void cancelButton_Click(object sender, RoutedEventArgs e)  
    {  
        if (cts != null)  
        {  
            cts.Cancel();  
        }  
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-19
      相关资源
      最近更新 更多