> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/construir/escrever-fluxos/como-executar-em-paralelo-e-agregar-resultados.md).

# Como executar em paralelo e agregar resultados

Como disparar vários commands ao mesmo tempo dentro de uma function, esperar todos com `WhenAll` ou o primeiro com `WhenAny`, e ler os resultados sem depender da ordem de término.

## Fan-out e fan-in com WhenAll

Consultar três fornecedores e escolher o menor preço:

```csharp
public record Cotacao(string Fornecedor, decimal Preco);

[SkailFunction]
public async SkailTask<Cotacao> MelhorCotacao(Guid produtoId)
{
    var a = CotarFornecedorA(produtoId);          // sem await: as três começam
    var b = CotarFornecedorB(produtoId);
    var c = CotarFornecedorC(produtoId);

    await SkailTask.WhenAll(a, b, c);             // espera as três

    var cotacoes = new[] { await a, await b, await c };   // resultado pela posição
    return cotacoes.OrderBy(x => x.Preco).First();
}

[SkailCommand(retryCount: 8)]
public async SkailTask<Cotacao> CotarFornecedorA(Guid produtoId)
    => new Cotacao("A", await _fornecedorA.CotarAsync(produtoId));

[SkailCommand(retryCount: 8)]
public async SkailTask<Cotacao> CotarFornecedorB(Guid produtoId)
    => new Cotacao("B", await _fornecedorB.CotarAsync(produtoId));

[SkailCommand(retryCount: 8)]
public async SkailTask<Cotacao> CotarFornecedorC(Guid produtoId)
    => new Cotacao("C", await _fornecedorC.CotarAsync(produtoId));
```

Os commands são criados sem `await`, para que os três iniciem; `WhenAll` espera todos; depois, `await a` devolve o resultado gravado de `a`, sem esperar de novo. Ler pela posição (`a`, `b`, `c`) é o que garante determinismo: a ordem em que terminaram não importa e não é reproduzível.

Cada ramificação grava o próprio passo. Se `b` falhar e esgotar as tentativas, a exceção sai do `WhenAll` na function; `a` e `c`, se concluíram, ficam gravados e não repetem na retomada.

## Quantidade variável

Uma lista de ids:

```csharp
[SkailFunction]
public async SkailTask EmitirLote(Guid[] pedidoIds)
{
    var emissoes = pedidoIds.Select(EmitirNota).ToArray();     // SkailTask[] na ordem dos ids
    await SkailTask.WhenAll(emissoes);

    for (var i = 0; i < pedidoIds.Length; i++)
    {
        var chave = await emissoes[i];
        await RegistrarChave(pedidoIds[i], chave);
    }
}

[SkailCommand]
public async SkailTask<string> EmitirNota(Guid pedidoId) { /* ... */ }
```

A ordem da lista é a ordem dos resultados. Lotes muito grandes geram muitos passos por execução; para milhares de itens, divida em sub-lotes ou faça uma function por item disparada por trigger. Ver [Limites e cotas](/aprender/garantias/limites-e-cotas.md).

## O primeiro que responder, com WhenAny

`WhenAny` devolve a primeira `SkailTask` concluída. O uso mais comum é dar prazo a uma espera ([Como esperar com timeout](/construir/escrever-fluxos/como-esperar-com-timeout.md)); o segundo é redundância: consultar dois provedores e usar o primeiro que responder.

```csharp
var primario   = ConsultarProvedorPrimario(cpf);
var secundario = ConsultarProvedorSecundario(cpf);

var vencedor = await SkailTask.WhenAny(primario, secundario);
var resultado = vencedor == primario ? await primario : await secundario;
```

A ramificação que perdeu continua em segundo plano até concluir ou até a function terminar. Um aviso real: com muitas execuções esperando e poucas instâncias, qual ramificação conclui primeiro depende da ordem em que o trabalho é processado, então `WhenAny` como "o mais rápido" precisa de instâncias suficientes para ser fiel à latência real.

## Paralelismo dentro de um command

Dentro de um `[SkailCommand]`, `Task.WhenAll` comum é permitido: o command roda uma vez e o que acontece dentro dele não é replayado. Use isso quando o paralelismo é um detalhe de implementação de um único efeito (baixar três arquivos para montar um relatório). Use `SkailTask.WhenAll` na function quando cada ramificação é um passo que merece o próprio checkpoint, retry e linha no Monitor.

## Erros comuns

| Sintoma                                | Causa                                                                             | Correção                                 |
| -------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------- |
| Resultados trocados entre fornecedores | Leu pela ordem de término, ou por uma lista montada em ordem diferente da criação | Leia pela posição da task que você criou |
| `SkailNonDeterministicException`       | `Task.WhenAll` na function em vez de `SkailTask.WhenAll`                          | Use as versões `SkailTask`               |
| As ramificações rodam em sequência     | `await` em cada chamada antes de passar ao `WhenAll`                              | Crie sem `await`, passe ao `WhenAll`     |

## Próximos passos

[Fan-out / Fan-in](/construir/padroes-e-antipadroes/fan-out-fan-in.md) é o padrão completo com agregação. A referência de [WhenAll e WhenAny](/construir/sdk-.net/whenall-e-whenany.md).
