> 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/construa/como-fazer/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;
* prazo para um command lento.

## Prazo fixo em uma espera

O caso base, detalhado em [Como esperar com timeout](/construa/como-fazer/esperar-um-evento-com-prazo.md):

{% tabs %}
{% tab title="C#" %}

```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;
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Skail, SkailCommand, SkailFunction } from "@skail/runtime";

const Eventos = { RespostaRecebida: "RESPOSTA_RECEBIDA" } as const;

export interface Resposta {
  Texto: string;
}

export class TimeoutService {
  @SkailFunction()
  async AguardarResposta(id: string): Promise<void> {
    const resposta = Skail.waitForEvent<Resposta>(Eventos.RespostaRecebida, id);
    const prazo = Skail.delayMinutes(24 * 60);

    const r = await Skail.whenAny(resposta, prazo);
    if (r === undefined) {
      await this.TratarExpiracao(id);
      return;
    }
    void r;
  }

  @SkailCommand() async TratarExpiracao(id: string): Promise<void> {}
}
```

{% endtab %}
{% endtabs %}

## 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:

{% tabs %}
{% tab title="C#" %}

```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);
}
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Skail, SkailCommand, SkailFunction } from "@skail/runtime";

const Eventos = { PagamentoRecebido: "PAGAMENTO_RECEBIDO" } as const;

export interface Pagamento {
  Valor: number;
}

export class FaturaService {
  @SkailFunction()
  async AcompanharFatura(faturaId: string, vencimentoIso: string): Promise<void> {
    const agora = new Date(await this.ObterDataAtual()).getTime(); // command
    const ateOVencimento = new Date(vencimentoIso).getTime() - agora;

    const pagamento = Skail.waitForEvent<Pagamento>(Eventos.PagamentoRecebido, faturaId);
    const prazo = Skail.delay(ateOVencimento > 0 ? ateOVencimento : 0);

    const resultado = await Skail.whenAny(pagamento, prazo);
    if (resultado === undefined) {
      await this.IniciarCobrancaEmAtraso(faturaId);
      return;
    }
    await this.RegistrarPagamento(faturaId, resultado);
  }

  @SkailCommand() async ObterDataAtual(): Promise<string> { return new Date().toISOString(); }
  @SkailCommand() async IniciarCobrancaEmAtraso(faturaId: string): Promise<void> {}
  @SkailCommand() async RegistrarPagamento(faturaId: string, pagamento: Pagamento): Promise<void> {}
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Se a function é iniciada depois do vencimento, `ateOVencimento` é negativo e o delay é zero: o prazo vence imediatamente e o fluxo executa o tratamento da expiração.
{% endhint %}

## 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 tratamento da expiração ainda roda, só que com atraso. Se o evento também chegou nesse intervalo, os dois estão concluídos na retomada e a espera com prazo 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:

{% tabs %}
{% tab title="C#" %}

```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;
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Skail, SkailCommand, SkailFunction } from "@skail/runtime";

const Eventos = { Decidido: "DECIDIDO" } as const;

export interface Decisao {
  Aprovado: boolean;
}

export class LembreteService {
  @SkailFunction()
  async AguardarDecisao(id: string): Promise<Decisao | undefined> {
    const decisao = Skail.waitForEvent<Decisao>(Eventos.Decidido, id);

    const lembrete = Skail.delayMinutes(2 * 24 * 60);
    const primeiro = await Skail.whenAny(decisao, lembrete);
    if (primeiro === undefined) {
      await this.EnviarLembrete(id);
      const restante = Skail.delayMinutes(24 * 60);
      const segundo = await Skail.whenAny(decisao, restante);
      if (segundo === undefined) {
        await this.TratarExpiracao(id);
        return undefined;
      }
    }
    const d = await decisao;
    return d;
  }

  @SkailCommand() async EnviarLembrete(id: string): Promise<void> {}
  @SkailCommand() async TratarExpiracao(id: string): Promise<void> {}
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Para várias mensagens em datas, [Régua de lembretes](/construa/como-fazer/padroes-e-antipadroes/regua-de-lembretes.md).
{% endhint %}

## 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](/construa/como-fazer/padroes-e-antipadroes/polling-de-sistema-externo.md). Para o timeout de uma chamada HTTP individual dentro do command, use o timeout do cliente HTTP: 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 espera 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

* Ler a hora atual na function para calcular o prazo (em C#, quebra o replay; em TypeScript, lança erro na hora).
* Espera sem prazo (execução parada para sempre).
* Prazo dentro do command com uma pausa longa da linguagem (ocupa o worker).

A lista completa está em [Antipadrões](/construa/como-fazer/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md).

## Próximos passos

[Tempo e agendamento](/construa/conceitos/tempo-e-agendamento.md) para o mecanismo do delay. [Human-in-the-loop](/construa/como-fazer/padroes-e-antipadroes/human-in-the-loop.md) para prazos em aprovações. A referência de [Delay](/referencia/sdk-csharp/delay.md) no SDK C#.
