Cancellation

Para cancelar en operaciones asíncronas debemos usar CancellationTokens, esto nos permite cancelar operaciones de larga duración.

No es habitual que cancelemos tareas y por tanto es interesante poder reutilizar los CancellationTokenSource, el objeto que genera CancellationToken.

Hasta la versión 6 no había seguridad de poder hacer esto ya que no sabíamos si todavía se hacía referencia a ese token.

En .NET 6 se amplía CancellationTokenSource con TryReset:

CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

private void CancelAction() {
  _cancellationTokenSource.Cancel();
}

public async Task DoSomeAwesomeWork() {
  if (!_cancellationTokenSource.TryReset()) {
    _cancellationTokenSource = new CancellationTokenSource();
  }
  var data = await FetchData(_cancellationTokenSource.Token);
}
  
public async Task<someObject> FetchData(CancellationToken token) {
  token.ThrowIfCancellationRequested();
  var client = new HttpClient();
  var response = await client.GetAsync("[YOUR_API]", token).ConfigureAwait(false);
  return await response.Content.ReadAsStringAsync(token).ConfigureAwait(false);
}

Una vez cancelado el token, no se podrá reciclar y TryReset no dará un false.

En el ejemplo anterior, si hemos realizado un CancelAction y luego hacemos nuestro DoSomeAwesomeWork, si no existe un token, instanciamos uno nuevo.

Por tanto, mientras no se cancele el token, podremos realizar un FetchData, pero si lo hubieramos cancelado, no podríamos traernos nada de la API a la que llamamos.

WaitAsync

De lo anterior seguro que casi no habéis aprendido nada nuevo, pero me sirve para recordar el token de cancelación. Y mostrar esta nueva funcionalidad que nos permite tener más control sobre cuando cancelar o sobre el tiempo de espera en operaciones asíncronas agregando el método WaitAsync.

Con WaitAsync podemos especificar un token de cancelación o un tiempo de espera para una tarea.

CancellationToken CancellationToken = _cancellationTokenSource.Token;
var client = new HttpClient();
var response = await client.GetAsync("[YOUR_API]", token).WaitAsync(token);

Tal y como apunta la documentación, tenemos tres sobrecargas:

    • WaitAsync (TimeSpan, CancellationToken)
    • WaitAsync (CancellationToken)
    • WaitAsync (TimeSpan)

Con TimeSpan: se completará cuando se complete la tarea o cuando expire el tiempo de espera especificado.

Con CanellationToken: se completará cuando se complete la tarea o cuando se solicite un CancellationToken.

Y cuidad con no confundir WaitAsync con Wait. Wait es una operación de bloqueo, es decir, bloqueará el hilo hasta que se completen las tareas y solo debemos usarlo en acciones muy específicas. Sin embargo, WaitAsync es una forma de agregar una directiva de cancelación o tiempo de espera a una tarea asíncrona que se ejecutará sin bloqueos.