Cancelling async tasks – C#

async await programming pattern has become very common these days. For developers working on Windows store apps there is no other option apart from learning this pattern.

Few days back I was working on an app with a simple control flow:

  • User starts the application
  • Using async await we try to start a long running process
  • Once process is completed, results are shown to the user

One feedback that we got was to allow user cancel this long running process if he wishes to do so. All we have to do is give user a progress indicator, progress text and a button to cancel the current long running async task.

Let’s see our existing code. In our view model we have a method that is used to load media files in a playlist;

public async Task<bool> AutoLoadPlaylist()
{
try
{
this.InProgress = true;
this.ProgressText = "We are trying to add your last saved playlist";
if (string.IsNullOrEmpty(AutoSavedPlayListPath))
return false;

var files = await _command.AddPlayList(AutoSavedPlayListPath);
await AddMusicFiles(files);
}
finally
{
this.InProgress = false;
}
return true;
}
 
Here we call another command which is responsible for actually creating this playlist;
 
public async Task<IList<StorageFile>> AddPlayList(string path)
{
try
{
var file = await StorageFile.GetFileFromPathAsync(path);
Playlist playList = await Playlist.LoadAsync(file);
return playList.Files;
}
catch (FileNotFoundException)
{
AutoSavedPlayListPath = string.Empty;
return null;
}
}

and, finally we call AutoLoadPlaylist(…) method from our view;

await this.viewModel.AutoLoadPlaylist();
 
Now, in order to provide a way to cancel this operation we need to change our code little bit to implement task cancellation pattern. This is done via CancellationTokenSource class defined in System.Threading namespace.
 
First, let’s modify our command class method to accept a cancellation token as method argument and then use that token while starting the task;
 

public async Task<IList<StorageFile>> AddPlayList(string path,

CancellationToken cancellationToken)
{
try
{
var file = await StorageFile.GetFileFromPathAsync(path);
Playlist playList = await Playlist
.LoadAsync(file)
.AsTask(cancellationToken);
return playList.Files;
}
catch (FileNotFoundException)
{
AutoSavedPlayListPath = string.Empty;
return null;
}
}

Now, modify the method in view model to accept cancellation token and then pass it on to command method. We also need to catch OperationCanceledException which is thrown when a task is cancelled.

public async Task<bool> AutoLoadPlaylist(CancellationToken cancellationToken)
{
try
{
this.InProgress = true;
this.ProgressText = "We are trying to add your last saved playlist";
if (string.IsNullOrEmpty(AutoSavedPlayListPath))
return false;

var files = await _command.AddPlayList(AutoSavedPlayListPath,

cancellationToken);
await AddMusicFiles(files);
}
catch (OperationCanceledException)
{
// no need to do anything
}
finally
{
this.InProgress = false;
}
return true;
}

Now modify the code that is calling view model’s AddPlayList(…) method and pass a cancellation token instance;

CancellationTokenSource cts;

CancellationToken _cancellationToken;


private async void InitializeControls()
{
try
{
cts = new CancellationTokenSource();
_cancellationToken = cts.Token;

await this.viewModel.AutoLoadPlaylist(_cancellationToken);
}
catch (Exception ex)
{
var dlg = new MessageDialog(ex.Message);
dlg.ShowAsync();
}
}
 
Infrastructure wise we are done. Only thing left now is to provide a button which user will click to cancel this long running operation. In your cancel button click (tapped) event handler add this code;
 
if (cts != null)
{
this.viewModel.ProgressText = "Trying to cancelling the operation";
cts.Cancel();
}

Make sure you cancel correct cancellation token source object 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s