> 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-esperar-com-timeout.md).

# Como esperar com timeout

Toda espera por evento externo leva um prazo. Este guia mostra o padrão `WhenAny` com `WaitForEvent` e `Delay`, o que fazer quando o prazo vence, e como enviar um lembrete antes de vencer.

## O padrão

```csharp
[SkailFunction]
public async SkailTask AguardarAprovacao(Guid solicitacaoId)
{
    await NotificarAprovador(solicitacaoId);                                  // command

    var aprovacao = SkailTask.WaitForEvent<Decisao>(Eventos.SolicitacaoDecidida, solicitacaoId.ToString());
    var prazo     = SkailTask.Delay(TimeSpan.FromDays(3));

    var vencedor = await SkailTask.WhenAny(aprovacao, prazo);

    if (vencedor == prazo)
    {
        await RegistrarExpiracao(solicitacaoId);                              // command
        return;
    }

    var decisao = await aprovacao;                                            // já concluída; devolve o payload
    if (decisao.Aprovada) await Executar(solicitacaoId);
    else                  await RegistrarRecusa(solicitacaoId, decisao.Motivo);
}
```

Quatro detalhes. As duas `SkailTask` são criadas sem `await` e passadas ao `WhenAny`; é ele que espera. `WhenAny` devolve a que concluiu primeiro; comparar com `prazo` diz se foi timeout. Depois do `WhenAny`, dar `await` na espera vencedora devolve o payload sem esperar de novo. E o `instanceId` é o id da solicitação, o mesmo que o fire vai usar.

O que acontece com a espera que perdeu: se o evento chegar depois do prazo, ele não acorda nada nesta execução, porque a function já seguiu. Se você precisa reagir a uma decisão atrasada, trate isso em quem dispara (o fire vai devolver que não havia espera) ou inicie outra execução.

## Decidir o que fazer no timeout

Depende do negócio, e a decisão é código na function: cancelar (o exemplo acima), aprovar por padrão (`if (vencedor == prazo) decisao = Decisao.AprovadaPorPrazo`), escalar para outra pessoa (notifica e espera de novo, com novo prazo), ou marcar como pendente para intervenção manual. Nenhuma dessas opções exige nada além de commands e outra rodada de `WhenAny`.

## Lembrete antes do prazo

Dois prazos em sequência: um curto para o lembrete, o restante para a decisão.

```csharp
var aprovacao = SkailTask.WaitForEvent<Decisao>(Eventos.SolicitacaoDecidida, solicitacaoId.ToString());

var lembrete = SkailTask.Delay(TimeSpan.FromDays(2));
if (await SkailTask.WhenAny(aprovacao, lembrete) == lembrete)
{
    await EnviarLembrete(solicitacaoId);                                      // command

    var restante = SkailTask.Delay(TimeSpan.FromDays(1));
    if (await SkailTask.WhenAny(aprovacao, restante) == restante)
    {
        await RegistrarExpiracao(solicitacaoId);
        return;
    }
}

var decisao = await aprovacao;
```

A mesma `aprovacao` participa dos dois `WhenAny`; ela continua pendente até ser atendida ou até a function terminar. O padrão completo, com várias mensagens, está em [Régua de lembretes](/construir/padroes-e-antipadroes/regua-de-lembretes.md).

## Prazo calculado a partir de uma data

Se o prazo é uma data de negócio (o vencimento da fatura), leia o relógio em um command e calcule o intervalo:

```csharp
var agora = await ObterDataAtual();                                           // command que devolve DateTime.UtcNow
var ateOVencimento = fatura.Vencimento - agora;
var prazo = SkailTask.Delay(ateOVencimento > TimeSpan.Zero ? ateOVencimento : TimeSpan.Zero);
```

`DateTime.UtcNow` direto na function quebra o replay. Ver [Tempo e agendamento](/aprender/fundamentos/tempo-e-agendamento.md).

## Erros comuns

| Sintoma                                      | Causa                                                          | Correção                                            |
| -------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------- |
| `SkailNonDeterministicException` na retomada | Prazo calculado com `DateTime.UtcNow` na function              | Relógio via command                                 |
| Timeout nunca dispara                        | `await` direto no `WaitForEvent` em vez de passar ao `WhenAny` | Crie as duas tasks sem `await` e passe ao `WhenAny` |
| Execução fica aguardando para sempre         | Sem prazo                                                      | Este guia                                           |
| Payload nulo depois do `WhenAny`             | Deu `await` no `prazo` em vez de na espera                     | `await aprovacao`                                   |

## Próximos passos

[Timeout e prazo](/construir/padroes-e-antipadroes/timeout-e-prazo.md) para as variações do padrão. [Human-in-the-loop](/construir/padroes-e-antipadroes/human-in-the-loop.md) para aprovações. A referência de [WhenAll e WhenAny](/construir/sdk-.net/whenall-e-whenany.md) e [WaitForEvent](/construir/sdk-.net/waitforevent.md).
