Pattern for cancel async method on reentry

321 Views Asked by At

I want to cancel an async function on reentrancy, so that the work do not get stacked up and unneeded work is prevented. e.g. my file scanning can take up to 8 seconds, but when I change the folder in the UI, the old function should be canceled. I saw samples with the CancellationToken, but it seems to me like too much code.

My approach is like this, and it seems to work, but it adds to much clutter to the code. Perhaps I´m also missing to catch the TaskCanceledException which would add more code.

private CancellationTokenSource scanFilesState;
private IList<FileInfo> files;

private async Task ScanFilesAsync(string path)
{
    // stop old running
    this.scanFilesState?.Cancel();
    this.scanFilesState?.Dispose();

    this.scanFilesState = new CancellationTokenSource();

    this.files = await FileHandler.ScanFilesAsync(path, this.scanFilesState.Token);

    this.scanFilesState?.Dispose();
    this.scanFilesState = null;
}

Is there a shorter or better way?

Is there a pattern to wrap this code up?

1

There are 1 best solutions below

7
JohnnyBravo75 On

I seems that I do not need the cleanup after, and came up with this approach and wrapped CancellationTokenSource to be safe in handling.

public class SafeCancellationTokenSource : IDisposable
    {
        private CancellationTokenSource state = new CancellationTokenSource();

        public CancellationTokenSource State => state;

        public CancellationToken Token => State.Token;

        public bool IsCancellationRequested => State.IsCancellationRequested;

        public void Cancel()
        {           
            this.state?.Cancel();
            this.state?.Dispose();
            this.state = new CancellationTokenSource();
        }

        public void Dispose()
        {
            this.state?.Dispose();
            this.state = null;
        }
    }

The code now looks like this

private SafeCancellationTokenSource scanFilesState =  new SafeCancellationTokenSource();
private IList<FileInfo> files;

private async Task ScanFilesAsync(string path)
{   
    this.scanFilesState.Cancel();
    this.files = await FileHandler.ScanFilesAsync(path, this.scanFilesState.Token);
}

Edit: added call to Dispose() again