馃晵 Task vs Promise: Encadenaci贸n

Oscar L贸pez | Jun 1, 2024 min read

El primer lenguaje en el que aprend铆 a trabajar de forma as铆ncrona fue JavaScript. Al principio, me cost贸 mucho trabajo porque era una forma de pensar completamente distinta a la que aprend铆 en la universidad. Una vez que logr茅 interiorizar los principios de la programaci贸n as铆ncrona, me fue mucho m谩s sencillo. Entonces, cuando comenc茅 a trabajar en C#, de inmediato detect茅 las similitudes entre las Task y las Promise, pues son pr谩cticamente equivalentes.

Pero al intentar encadenar promesas de la misma forma en la que se hace en JavaScript, me top茅 con una peculiaridad. La funci贸n que se recibe en el m茅todo .then de JavaScript es una funci贸n que espera el valor que est谩 envuelto en la promesa. Es decir, si tenemos una Promise<number>, la funci贸n del .then es una funci贸n que recibe un number. En cambio, en C# el “equivalente” a .then es .ContinueWith, pero este m茅todo espera una funci贸n que recibe una Task del mismo tipo que la Task original. Es decir, si tenemos un Task<string>, el m茅todo .ContinueWith recibe una funci贸n que recibe un Task<string>. Esto me caus贸 mucha confusi贸n, y conversando con ChatGPT logr茅 tener m谩s claridad en el caso.

En caso de querer revisar mi proceso, esta es la conversaci贸n

.then en Javascript

En JavaScript, el m茅todo .then se utiliza para manejar el resultado de una promesa. El manejador de .then recibe directamente el valor resuelto de la promesa. Adem谩s, JavaScript proporciona el m茅todo .catch para manejar errores.

Ejemplo en JavaScript:

fetch('http://example.com')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

En este ejemplo, si la promesa se resuelve, el manejador en el primer .then recibe el response y lo procesa. Si ocurre un error, el manejador en .catch se ejecuta.

.ContinueWith en C#

En C#, el m茅todo .ContinueWith de una Task se utiliza para continuar con la ejecuci贸n de c贸digo despu茅s de que una tarea se complete. A diferencia de .then, el manejador de .ContinueWith recibe una instancia de Task<T>, lo que permite acceder a m谩s detalles acerca de la tarea, incluyendo su estado, excepciones y resultado.

Ejemplo b谩sico en C#:

Task<int> task = Task.Run(() => {
    // Simulando una operaci贸n as铆ncrona
    return 42;
});

task.ContinueWith(t => {
    if (t.IsFaulted)
    {
        // Manejar excepciones
        Console.WriteLine($"Error: {t.Exception.InnerException.Message}");
    }
    else if (t.IsCompletedSuccessfully)
    {
        // Manejar el resultado exitoso
        Console.WriteLine($"Resultado: {t.Result}");
    }
});

En este ejemplo, ContinueWith maneja tanto el resultado exitoso como las posibles excepciones. Esto es posible porque ContinueWith proporciona acceso a la tarea completa.

El porqu茅

No hay .catch en C#

En C#, no existe un m茅todo equivalente a .catch de las promesas en JavaScript que se encadene directamente a una Task. En cambio, los errores se manejan dentro del mismo manejador ContinueWith o utilizando bloques try-catch en combinaci贸n con await.

Opciones de .ContinueWith en C#

El m茅todo .ContinueWith tambi茅n permite especificar opciones que controlan cu谩ndo se debe ejecutar el manejador de continuaci贸n, tales como OnlyOnRanToCompletion y OnlyOnFaulted.

Ejemplo con opciones de ContinueWith:

Task<int> task = Task.Run(() => {
    // Simulando una operaci贸n que puede lanzar una excepci贸n
    throw new InvalidOperationException("Error simulado");
    return 42;
});

task.ContinueWith(t => {
    Console.WriteLine($"Resultado: {t.Result}");
}, TaskContinuationOptions.OnlyOnRanToCompletion);

task.ContinueWith(t => {
    Console.WriteLine($"Error: {t.Exception.InnerException.Message}");
}, TaskContinuationOptions.OnlyOnFaulted);

En este ejemplo, se definen dos manejadores de continuaci贸n: uno que se ejecuta solo si la tarea se completa con 茅xito (OnlyOnRanToCompletion) y otro que se ejecuta solo si la tarea falla (OnlyOnFaulted).

Conclusiones

Aunque tanto .ContinueWith en C# como .then en JavaScript sirven para continuar con la ejecuci贸n de c贸digo despu茅s de una operaci贸n as铆ncrona, hay diferencias importantes:

  1. Manejador de Continuaci贸n: En JavaScript, el manejador .then recibe el valor resuelto de la promesa. En C#, el manejador .ContinueWith recibe una instancia de Task<T>, proporcionando acceso a m谩s detalles de la tarea.
  2. Manejo de Errores: JavaScript utiliza .catch para manejar errores. En C#, se manejan dentro del manejador ContinueWith o mediante bloques try-catch cuando se usa await.
  3. Opciones de Continuaci贸n: C# permite especificar opciones en .ContinueWith para controlar cu谩ndo se debe ejecutar el manejador de continuaci贸n, ofreciendo un control m谩s granular.

Estas diferencias reflejan las distintas filosof铆as y capacidades de los lenguajes, proporcionando a los desarrolladores herramientas poderosas para manejar operaciones as铆ncronas en cada entorno.

Espero que este art铆culo te sea 煤til para entender mejor las diferencias entre .ContinueWith en C# y .then en JavaScript, as铆 como las opciones para manejar errores y acceder a los detalles de las tareas en C#.

comments powered by Disqus