Crear una tarea completada <T>

125

Estoy implementando un método Task<Result> StartSomeTask()y ya sé el resultado antes de que se llame al método. ¿Cómo creo una Tarea <T> que ya se ha completado?

Esto es lo que estoy haciendo actualmente:

private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var task = new Task<Result>(() => theResult);
    task.RunSynchronously(CurrentThreadTaskScheduler.CurrentThread);
    return task;
}

¿Hay una mejor solución?

dtb
fuente
66
Tenga en cuenta que las respuestas a esta pregunta también funcionan bien para crear una tarea simple (no <T>) porque la tarea <T> hereda de la tarea.
Tim Lovell-Smith
Tenga en cuenta que hoy hay ValueTasktareas completadas (es decir, valores que ya tiene para que el código sea esencialmente sincrónico), lo que le ahorrará una asignación.
nawfal

Respuestas:

111
private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var taskSource = new TaskCompletionSource<Result>();
    taskSource.SetResult(theResult);
    return taskSource.Task;
}
QrystaL
fuente
@DanielLobo puede obtener una respuesta si explica cuál es su objeción
user2023861
1
¿No debería ser el siguiente más simple y con muchos más votos? @ user2023861
Daniel Lobo
203

Al apuntar a .NET 4.5 puede usar Task.FromResult:

public static Task<TResult> FromResult<TResult>(TResult result);

Para crear una tarea fallida, use Task.FromException:

public static Task FromException(Exception exception);
public static Task<TResult> FromException<TResult>(Exception exception);

.NET 4.6 agrega Task.CompletedTasksi necesita un no genérico Task.

public static Task CompletedTask { get; }

Soluciones para versiones anteriores de .NET:

  • Cuando apunte a .NET 4.0 con Async Targetting Pack (o AsyncCTP), puede usar TaskEx.FromResulten su lugar.

  • Para obtener no genéricos Taskantes de .NET 4.6, puede utilizar el hecho que se Task<T>deriva de Tasky simplemente llamar Task.FromResult<object>(null)o Task.FromResult(0).

CodesInChaos
fuente
13
Para devolver una tarea no genérica, es mejor usar algo como Task.FromResult (0). Usar "nulo" como parámetro puede confundir al compilador que no puede determinar el parámetro genérico.
Whyllee
¿Qué pasa con las excepciones? Los métodos asíncronos se compilan en una máquina de estado que captura excepciones y las guarda en la Tarea devuelta. Esto sucede incluso para la ejecución del código antes de la primera espera. El método que devuelve Task.FromResult podría generar excepciones directamente.
Robert Važan
@ RobertVažan Un caso interesante. Podría decirse que si está recuperando su resultado conocido de un método y ese método arroja excepciones, entonces hay un defecto que debe corregirse.
Gusdor
1
@ RobertVažan Puede escribir fácilmente su propio FromExceptionmétodo, que se comporta como FromResultpero representa una tarea defectuosa. Tal método puede simplemente devolverlo para sus casos de error si es importante que la excepción se represente en la tarea resultante.
Servicio
1
Task.FromException no está disponible en .NET 4.5 ... Creo que debería especificarse.
STiLeTT
12

Para tareas sin valor de retorno, .NET 4.6 ha agregado Task.CompletedTask .

Devuelve una tarea que ya está en TaskStatus.RanToCompletion. Probablemente devuelve la misma instancia cada vez, pero la documentación le advierte que no cuente con ese hecho.

Daryl
fuente
1

Si está utilizando Rx, una alternativa es Observable.Return (resultado) .ToTask ().

Niall Connaughton
fuente
1

Llamar a Task.WhenAll sin ningún parámetro devolverá una tarea completada.

Task task = Task.WhenAll();
zumalifeguard
fuente
Si bien esto funcionará, es una solución oscura que podría confundir a las personas al leer el código, ya que implica esperar tareas que no existen
Adrian Hristov