async task cancellation c# xamarin

流过昼夜 提交于 2019-12-21 04:31:16

问题


I have a functionality of search users. I have provided a textview and on that textview changed method I'm firing a method to get data from web server. But I'm facing problem when user types letter, because all the api hits done in async task. Service should be hit after 100 milli-sec of wait, means if user types a letter "a" then doesn't type for 100 milli-sec then We have to hit the service. But if user types "a" then "b" then "c", so one service should be hit for "abc", not for all.

I followed the official link, but it doesn't help me https://msdn.microsoft.com/en-us/library/jj155759.aspx

So basically here is my code

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

Here is my method to GetUser

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

回答1:


In your example, you are creating a CancellationTokenSource - you need to hold a reference to it, so that the next time the handler is invoked, the previous search can be cancelled. Here is an example console app that you should be able to run, but the important piece is in the handler.

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

Be sure to pass the CancellationToken into all of the async methods, including those that perform the web request, this way you signal the cancellation right down to the lowest level.




回答2:


Try to use timer. First time then you change text - you create it. Then you change text after that - you restart timer. If you don't change text for 700 milliseconds - timer will fire PerformeSearch method. Use Timeout.Infinite for timer period parameter to prevent it from restarting.

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



回答3:


Instantiate the CancellationTokenSource.

cts = new CancellationTokenSource(); Example method

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


来源:https://stackoverflow.com/questions/35099265/async-task-cancellation-c-sharp-xamarin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!