> 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/padroes-e-antipadroes/timeout-e-prazo.md).

# Timeout e prazo

Toda espera tem um prazo, e o que acontece quando ele vence é decisão de negócio escrita na function. Este padrão reúne as variações: prazo fixo, prazo em data, prazo que já venceu na retomada, lembrete antes de vencer, e prazo para um command lento.

## Prazo fixo em uma espera

O caso base, detalhado em [Como esperar com timeout](/construir/escrever-fluxos/como-esperar-com-timeout.md):

```csharp
var resposta = SkailTask.WaitForEvent<Resposta>(Eventos.RespostaRecebida, id.ToString());
var prazo    = SkailTask.Delay(TimeSpan.FromHours(24));

if (await SkailTask.WhenAny(resposta, prazo) == prazo) { await TratarExpiracao(id); return; }
var r = await resposta;
```

## Prazo em uma data de negócio

O prazo é o vencimento da fatura, não "24 horas a partir de agora". O relógio vem de um command e o intervalo é calculado uma vez:

```csharp
[SkailFunction]
public async SkailTask AcompanharFatura(Guid faturaId, DateTime vencimento)
{
    var agora = await ObterDataAtual();                      // command
    var ateOVencimento = vencimento - agora;

    var pagamento = SkailTask.WaitForEvent<Pagamento>(Eventos.PagamentoRecebido, faturaId.ToString());
    var prazo     = SkailTask.Delay(ateOVencimento > TimeSpan.Zero ? ateOVencimento : TimeSpan.Zero);

    if (await SkailTask.WhenAny(pagamento, prazo) == prazo) { await IniciarCobrancaEmAtraso(faturaId); return; }
    await RegistrarPagamento(faturaId, await pagamento);
}
```

Se a function é iniciada depois do vencimento, `ateOVencimento` é negativo e o delay é zero: o prazo vence imediatamente e o fluxo segue pelo caminho da expiração, a menos que o evento de pagamento já tenha sido disparado, caso em que ele vence a corrida. Os dois comportamentos são os corretos.

## Prazo que já venceu durante uma indisponibilidade

Se a sua aplicação ficou fora por seis horas e um prazo venceu nesse intervalo, a execução é retomada assim que um worker voltar, e o `Delay` está concluído: o caminho da expiração roda com atraso, mas roda. Se o evento também chegou nesse intervalo, os dois estão concluídos na retomada e o `WhenAny` devolve o que concluiu primeiro conforme o registro. Nenhum dos dois se perde.

## Lembrete antes do prazo

Dois delays em sequência sobre a mesma espera:

```csharp
var decisao  = SkailTask.WaitForEvent<Decisao>(Eventos.Decidido, id.ToString());

var lembrete = SkailTask.Delay(TimeSpan.FromDays(2));
if (await SkailTask.WhenAny(decisao, lembrete) == lembrete)
{
    await EnviarLembrete(id);
    var restante = SkailTask.Delay(TimeSpan.FromDays(1));
    if (await SkailTask.WhenAny(decisao, restante) == restante) { await TratarExpiracao(id); return; }
}
var d = await decisao;
```

Para várias mensagens em datas, [Régua de lembretes](/construir/padroes-e-antipadroes/regua-de-lembretes.md).

## Prazo para um command lento

Um command não deve demorar horas: ele ocupa um slot de execução do worker enquanto roda. Se a operação externa é longa (um relatório que o parceiro gera em 20 minutos, uma emissão que a prefeitura processa em horas), transforme em duas partes: um command que inicia a operação e devolve um identificador, e uma espera por evento (o parceiro chama o seu webhook) ou um polling com `Delay` entre consultas. Ver [Polling de sistema externo](/construir/padroes-e-antipadroes/polling-de-sistema-externo.md). Para o timeout de uma chamada HTTP individual dentro do command, use o timeout do `HttpClient`: isso é responsabilidade do command, não do skail.

## Escolher o que fazer no timeout

| Situação                               | Ação típica                                                                     |
| -------------------------------------- | ------------------------------------------------------------------------------- |
| Aprovação humana não veio              | Cancelar, escalar para outra pessoa, ou aprovar por padrão, conforme a política |
| Pagamento não confirmado               | Liberar a reserva, iniciar cobrança em atraso                                   |
| Webhook de parceiro não chegou         | Consultar o parceiro por polling antes de declarar falha                        |
| Resposta de agente ou usuário não veio | Encerrar a conversa e registrar                                                 |

Em todos os casos a ação é um ou mais commands e, se preciso, outra rodada de `WhenAny` com novo prazo. Registre a expiração como um command explícito: isso a torna visível no Monitor e no relatório.

## O que não fazer

`DateTime.UtcNow` na function para calcular o prazo (quebra o replay). Espera sem prazo (execução parada para sempre). Prazo dentro do command com `Task.Delay` longo (ocupa o worker). A lista completa está em [Antipadrões](/construir/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md).

## Próximos passos

[Tempo e agendamento](/aprender/fundamentos/tempo-e-agendamento.md) para o mecanismo do `Delay`. [Human-in-the-loop](/construir/padroes-e-antipadroes/human-in-the-loop.md) para prazos em aprovações. A referência de [Delay](/construir/sdk-.net/delay.md).
