Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TaskCanceledException when calling Task.Delay with a CancellationToken in an keyboard event

Tags:

I am trying to delay the processing of a method (SubmitQuery() in the example) called from an keyboard event in WinRT until there has been no further events for a time period (500ms in this case).

I only want SubmitQuery() to run when I think the user has finished typing.

Using the code below, I keep getting a System.Threading.Tasks.TaskCanceledException when Task.Delay(500, cancellationToken.Token); is called. What am I doing wrong here please?

CancellationTokenSource cancellationToken = new CancellationTokenSource();  private async void SearchBox_QueryChanged(SearchBox sender, SearchBoxQueryChangedEventArgs args) {          cancellationToken.Cancel();         cancellationToken = new CancellationTokenSource();      await Task.Delay(500, cancellationToken.Token);      if (!cancellationToken.IsCancellationRequested)     {         await ViewModel.SubmitQuery();     } } 
like image 346
Andrew Roberts Avatar asked Dec 11 '13 01:12

Andrew Roberts


People also ask

Which method can you use to cancel an ongoing operation that uses CancellationToken?

The CancellationTokenSource token is used to signal that the Task should cancel itself. In the above case, the operation will just end when cancellation is requested via Cancel() method.

Does task delay use a thread?

Task. Delay() is asynchronous. It doesn't block the current thread. You can still do other operations within current thread.

What is CancellationToken CancellationToken?

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects. You create a cancellation token by instantiating a CancellationTokenSource object, which manages cancellation tokens retrieved from its CancellationTokenSource. Token property.

Does task delay block?

Delay will create a task which will complete after a time delay. Task. Delay is not blocking the calling thread so the UI will remain responsive.


1 Answers

If you add ContinueWith() with an empty action, the exception isn't thrown. The exception is caught and passed to the tsk.Exception property in the ContinueWith(). But It saves you from writing a try/catch that uglifies your code.

await Task.Delay(500, cancellationToken.Token).ContinueWith(tsk => { }); 

UPDATE:

Instead of writing code to handle the exception, a boolean would be much cleaner. This is only preferred when a delay cancel is expected!. One way is to create a helper class (Although I don't like helper classes much)

namespace System.Threading.Tasks {     public static class TaskDelay     {         public static Task<bool> Wait(TimeSpan timeout, CancellationToken token) =>             Task.Delay(timeout, token).ContinueWith(tsk => tsk.Exception == default);          public static Task<bool> Wait(int timeoutMs, CancellationToken token) =>             Task.Delay(timeoutMs, token).ContinueWith(tsk => tsk.Exception == default);     } } 

For example:

var source = new CancellationTokenSource();  if(!await TaskDelay.Wait(2000, source.Token)) {     // The Delay task was canceled. } 

(don't forget to dispose the source)

like image 134
Jeroen van Langen Avatar answered Sep 20 '22 04:09

Jeroen van Langen