Crie uma tarefa concluída <T>

125

Estou implementando um método Task<Result> StartSomeTask()e já sei o resultado antes que o método seja chamado. Como crio uma tarefa <T> que já foi concluída?

Isto é o que estou fazendo atualmente:

private readonly Result theResult = new Result();

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

Existe uma solução melhor?

dtb
fonte
6
Observe que as respostas a esta pergunta também funcionam bem para criar uma tarefa simples (sem <T>) porque a tarefa <T> herda da tarefa.
Tim Lovell-Smith #
Observe que hoje existem ValueTasktarefas concluídas (ou seja, valores que você já possui para que o código seja essencialmente síncrono), o que salvará uma alocação.
Nawfal # 28/18

Respostas:

111
private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var taskSource = new TaskCompletionSource<Result>();
    taskSource.SetResult(theResult);
    return taskSource.Task;
}
QrystaL
fonte
@DanielLobo, você pode obter uma resposta se explicar qual é a sua objeção #
user2023861
1
Não deveria ser o abaixo mais simples e com muito mais votos positivos? @ user2023861
Daniel Lobo /
203

Ao direcionar o .NET 4.5, você pode usar Task.FromResult:

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

Para criar uma tarefa com falha, use Task.FromException:

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

O .NET 4.6 adiciona Task.CompletedTaskse você precisar de um não genérico Task.

public static Task CompletedTask { get; }

Soluções alternativas para versões mais antigas do .NET:

  • Ao direcionar o .NET 4.0 com o Async Targetting Pack (ou AsyncCTP), você pode usá-lo TaskEx.FromResult.

  • Para ficar não genérico Taskantes do .NET 4.6, você pode usar o fato de que Task<T>deriva Taske apenas chamar Task.FromResult<object>(null)or Task.FromResult(0).

CodesInChaos
fonte
13
Para retornar uma tarefa não genérica, é melhor usar algo como Task.FromResult (0). Usar "null" como parâmetro pode confundir o compilador, que não pode determinar o parâmetro genérico.
Whyllee
E as exceções? Os métodos assíncronos são compilados na máquina de estado que captura exceções e as salva na tarefa retornada. Isso acontece mesmo para a execução do código antes da primeira espera. O método que retorna Task.FromResult pode gerar exceções diretamente.
Robert Važan
@ RobertVažan Um caso interessante. Indiscutivelmente, se você está recuperando o resultado conhecido de um método e esse método lança exceções, existe um defeito que precisa ser corrigido.
Gusdor
1
@ RobertVažan Você pode escrever facilmente seu próprio FromExceptionmétodo, que se comporta como, FromResultmas representa uma tarefa com falha. Esse método pode simplesmente retornar isso para seus casos de erro, se for importante que a exceção seja representada na tarefa resultante.
precisa saber é
1
Task.FromException não está disponível no .NET 4.5 ... Acho que deve ser especificado.
Stilett
12

Para tarefas sem valor de retorno, o .NET 4.6 adicionou Task.CompletedTask .

Ele retorna uma tarefa que já está em TaskStatus.RanToCompletion. Provavelmente, sempre retorna a mesma instância, mas a documentação avisa para você não contar com esse fato.

Daryl
fonte
1

Se você estiver usando o Rx, uma alternativa é Observable.Return (result) .ToTask ().

Niall Connaughton
fonte
1

Chamar Task.WhenAll sem nenhum parâmetro retornará uma tarefa concluída.

Task task = Task.WhenAll();
zumalifeguard
fonte
enquanto isso vai funcionar, é uma solução obscura que pode confundir as pessoas quando a leitura do código, uma vez que implica esperando para tarefas que não existem
Adrian Hristov