Fechar uma tela não significa que uma operação foi interrompida
Ao cancelar uma geração, o usuário geralmente espera que o trabalho pare. No entanto, a ação visível — clicar em um botão, mudar de tela ou deixar de esperar — descreve apenas o que aconteceu na interface. Por si só, ela não demonstra o que aconteceu com a solicitação HTTP, com o processo no servidor ou com o processamento que um provedor externo talvez esteja realizando. São camadas diferentes, e cada uma pode ter seu próprio ciclo de vida.
Por isso, uma integração deve especificar o que significa «cancelar» em cada limite. O navegador pode parar de esperar por uma resposta; o servidor pode encerrar uma tarefa que controla; e uma API remota pode oferecer, ou não, um mecanismo documentado para cancelar uma operação aceita. Se uma camada não propagar o sinal ou não souber qual operação deve interromper, o trabalho pode continuar nela mesmo que o usuário já não veja o resultado.
A consequência prática é importante: não apresente um cancelamento como se fosse uma resposta válida e não informe que o provedor interrompeu o processamento sem ter verificado isso. O aplicativo pode confirmar que recebeu uma solicitação de cancelamento e que deixou de esperar localmente. Qualquer afirmação mais abrangente exige evidências sobre o comportamento da integração específica, incluindo a documentação do provedor e, quando possível, um teste observável.
Defina o que será cancelado antes de escrever código
Um sinal de cancelamento precisa ter um escopo explícito. Ele pode afetar a espera da interface, uma conexão HTTP, uma tarefa de backend ou uma operação identificável no serviço remoto. Em um sistema simples, talvez você controle apenas os dois primeiros níveis. Em uma arquitetura com um servidor intermediário, o cancelamento no navegador não interrompe automaticamente o trabalho desse servidor: é necessário que o aplicativo detecte a desconexão ou receba um sinal explícito e o converta em uma ação sobre a tarefa correspondente.
Também é importante separar os motivos. Um cancelamento explícito acontece quando o usuário pede para interromper o trabalho. O abandono pode ser uma navegação ou desconexão sem uma ação deliberada. Um timeout ocorre quando um prazo configurado é atingido. Os três casos podem fazer com que o aplicativo pare de esperar, mas não significam a mesma coisa, nem sempre devem ser apresentados da mesma forma e não necessariamente permitem saber se o trabalho remoto terminou.
Defina estados terminais que descrevam aquilo que o aplicativo verificou, e não aquilo que presume. Por exemplo: concluído, cancelamento solicitado, prazo local excedido e desconexão observada. Se o sistema conseguir confirmar um cancelamento remoto por meio de um mecanismo documentado, poderá registrar esse resultado separadamente. Manter os estados distintos melhora tanto a interface quanto o diagnóstico posterior.
Decisão de acordo com o evento observado
A tabela sugere o que registrar e comunicar. Ela não presume que um evento local tenha interrompido o processamento remoto.
| Evento | O que se sabe | Ação prudente |
|---|---|---|
| O usuário clica em «Cancelar» | Foi solicitada a interrupção da operação pela interface. | Registrar a solicitação e propagá-la às camadas que aceitam cancelamento. |
| O prazo local expira | O aplicativo parou de esperar dentro do prazo configurado. | Registrar um timeout; não classificá-lo como cancelamento remoto confirmado. |
| A tela é fechada ou o usuário navega para outra página | A tela deixou de estar disponível; a intenção do usuário pode ser ambígua. | Limpar a interface e definir explicitamente se a tarefa deve ser cancelada. |
| A conexão é perdida | A comunicação foi interrompida ou deixou de ser observada. | Registrar a desconexão e verificar quais processos continuam ativos. |
| A resposta chega ao mesmo tempo que o sinal de cancelamento | Há uma corrida entre dois eventos terminais. | Aplicar uma regra determinística de precedência e registrar o resultado escolhido. |
Propague o sinal pelos limites que você controla
No navegador, AbortController permite criar um sinal que pode ser associado a uma solicitação feita com fetch. Se o sinal for abortado, o cliente pode interromper a espera por essa solicitação. Isso resolve uma parte do problema: o código da interface deixa de tratar a resposta como uma operação que continua aguardando. Por si só, isso não demonstra que uma tarefa iniciada além da conexão tenha sido interrompida.
O próximo limite costuma ser o backend. Se o navegador se comunica com um serviço próprio, esse serviço precisa de uma política para associar a solicitação recebida ao trabalho que iniciou. Dependendo da plataforma e das bibliotecas utilizadas, ele poderá observar um sinal de cancelamento, uma desconexão ou ambos. O aplicativo deve verificar as capacidades reais do servidor e do cliente HTTP: não deve presumir que uma abstração propaga o cancelamento só porque a interface tem um botão.
Se o backend chama uma API externa, é necessário verificar se o cliente HTTP utilizado aceita um sinal de cancelamento e o que acontece quando ele é usado. Em seguida, deve-se consultar a documentação da API específica para saber se ela oferece um mecanismo para cancelar uma operação já aceita ou se só é possível interromper a conexão local. São capacidades diferentes. Algumas APIs oferecem uma operação explícita de cancelamento para operações específicas, mas isso não permite generalizar para serviços de geração de IA.
As diretrizes de projeto para APIs assíncronas também apoiam a ideia de que uma operação em andamento deve ter uma forma de cancelamento. Esse é um princípio de projeto, não uma garantia de que qualquer dependência ou provedor implemente essa capacidade, nem uma receita para propagá-la entre sistemas. Documente, camada por camada, quem recebe o sinal, que ação pode realizar e que resultado consegue observar.
Um fluxo de cancelamento que pode ser auditado
Desenhe o percurso antes de implementar a interface. Se uma camada não tiver capacidade de cancelamento, registre essa limitação em vez de representar o percurso como concluído.
- 01A interface registra o motivo: ação explícita, navegação, timeout ou outra desconexão observada.
- 02O cliente interrompe a espera local e comunica o cancelamento ao backend quando a arquitetura permite.
- 03O backend identifica a tarefa associada à solicitação e pede sua interrupção se a biblioteca utilizada oferecer esse recurso.
- 04O backend verifica se a API remota oferece uma operação de cancelamento documentada e só a utiliza dentro do escopo especificado.
- 05Cada camada registra o resultado que pode observar: solicitação recebida, conexão fechada, tarefa local interrompida ou cancelamento remoto confirmado.
Exemplo com AbortController no navegador
O exemplo a seguir inicia uma solicitação para uma rota própria do backend e permite cancelar a espera do navegador. Ele pressupõe que essa rota receba um objeto com um campo prompt e, em caso de sucesso, retorne JSON com um campo text. A rota é ilustrativa: o backend deve implementar o contrato e decidir separadamente como tratar a solicitação, propagar o cancelamento e determinar o que pode confirmar sobre a API externa.
A função verifica um sinal que já tenha sido abortado antes de iniciar o envio e evita atualizar a interface com uma resposta se o cancelamento for registrado primeiro. Em uma interface real, associe a função de cancelamento ao botão correspondente e desative-o quando a operação chegar a um estado terminal. O exemplo não demonstra que o backend ou o provedor interromperam o processamento.
Quem chama a função mantém o controlador para poder cancelar. Se a função generate for chamada com o sinal já abortado, ele será detectado antes do envio da solicitação.
async function generate(prompt, output, signal) {
if (signal.aborted) {
output.textContent = "Cancelamento solicitado antes do envio.";
return;
}
output.textContent = "Gerando…";
try {
const response = await fetch("/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt }),
signal
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (!signal.aborted) output.textContent = result.text;
} catch (error) {
if (signal.aborted || error.name === "AbortError") {
output.textContent = "A espera pela resposta foi interrompida.";
return;
}
output.textContent = "Não foi possível concluir a solicitação.";
}
}
const controller = new AbortController();
const task = generate("Resuma o documento", document.querySelector("#output"), controller.signal);
// Chame controller.abort() na ação de cancelar.
// Mantenha task se precisar aguardar o término do código local.
Trate o timeout como um prazo, não como prova de cancelamento
Um timeout define por quanto tempo uma camada está disposta a esperar. Pode haver um limite na interface, outro no servidor e outro no cliente HTTP que se conecta ao provedor. Esses limites devem ser coordenados de propósito: se uma camada abandonar a operação antes de outra sem comunicar isso, o trabalho pode continuar consumindo recursos em uma camada que ainda o considera ativo.
Quando um prazo expirar, registre qual relógio venceu e em que camada. «Timeout do navegador» é mais preciso do que «o modelo excedeu o tempo», a menos que haja evidência de que o serviço remoto comunicou esse resultado. O aplicativo pode parar de esperar, solicitar a interrupção às camadas disponíveis e exibir um estado de prazo excedido. Não deve transformar essa ação em uma afirmação de cancelamento confirmado.
Os efeitos sobre uso ou faturamento dependem da API e de suas condições. A documentação de uma API específica pode descrever o que a operação dela cancela; não se deve inferir a partir disso o comportamento de outro endpoint ou de outro provedor. Se esse efeito for importante, encontre a documentação oficial específica, verifique qual unidade de trabalho é cancelada e valide o resultado por meio de um teste controlado. Se a documentação não esclarecer o assunto, mantenha essa incerteza no produto e na política de custos.
Evite estados incoerentes quando houver uma corrida
A resposta e o cancelamento podem chegar quase ao mesmo tempo. Se a interface processar os dois eventos sem uma regra comum, poderá exibir «cancelado» primeiro e, depois, substituí-lo por uma resposta atrasada. Também pode acontecer o contrário: apresentar uma resposta que chegou depois de o usuário já ter pedido para interromper a operação. A solução não é adivinhar qual evento ocorreu primeiro no sistema remoto, mas definir qual estado terminal deve prevalecer no aplicativo.
Uma regra simples para a interface é fazer com que a primeira transição terminal registrada para uma operação impeça atualizações posteriores dessa mesma operação. Outra opção é atribuir um identificador a cada solicitação e descartar respostas de uma operação que já não está ativa. A escolha deve ser coerente com o produto: se for permitido manter uma resposta concluída apesar de um cancelamento tardio, essa política também deve ser explícita.
Não misture a apresentação com a verdade operacional. A interface pode marcar a operação como cancelada localmente, enquanto o backend registra que não foi possível confirmar o cancelamento remoto. Quando forem relevantes, mantenha esses dados separados nos registros ou estados internos. Assim, evita-se que uma resposta atrasada pareça provar que o cancelamento foi desfeito, ou que o fechamento de uma conexão pareça indicar que o trabalho foi concluído.
Teste cada fase e cada limite
Um teste que só clica em cancelar depois de exibir uma resposta não verifica o cancelamento durante o trabalho. Crie casos separados para os momentos relevantes e observe tanto a interface quanto o backend. Se houver uma API externa, verifique apenas aquilo que a integração e a documentação permitem observar; não use o desaparecimento do resultado na tela como indicador de que o provedor parou de processar.
Inclua um caso de cancelamento antes do envio: a operação não deve iniciar a solicitação se o sinal já estiver cancelado quando a função for chamada. Teste também o cancelamento depois de iniciar a solicitação, mas antes de receber uma resposta, e durante uma resposta em andamento se o endpoint usar um modo que permita observar esse intervalo. Verifique o comportamento ao fechar ou abandonar a tela de acordo com a política definida pelo produto.
Por fim, introduza uma corrida entre resposta e cancelamento e confira se o estado terminal é determinístico. Para um timeout, confirme que ele é registrado como prazo excedido, e não como uma ação explícita do usuário. Em cada teste, verifique se ainda há tarefas e conexões locais abertas e se o backend mantém algum trabalho ativo. Se não for possível inspecionar uma atividade remota, registre isso como uma limitação, em vez de afirmar que ela foi interrompida.
Matriz mínima de testes
Adapte os elementos observáveis à arquitetura. A última coluna descreve uma verificação local ou documental, não uma garantia universal do provedor.
| Cenário | Verificação no aplicativo | Limite a documentar |
|---|---|---|
| Sinal cancelado antes de chamar a função | A solicitação não é iniciada; a interface mantém um estado coerente. | A função e quem a chama respeitam o sinal antes do envio. |
| Cancelamento durante a espera | A interface para de esperar e não trata o aborto como uma resposta válida. | O que o backend pode observar e interromper. |
| Abandono da tela | A política definida é aplicada e uma tela que já não corresponde à operação não é atualizada. | Se o backend recebe algum sinal ou mantém a tarefa. |
| Timeout | O vencimento do prazo é registrado na camada correta. | Se a solicitação ou o trabalho remoto podem continuar ativos. |
| Resposta simultânea ao cancelamento | A regra de precedência produz um único estado terminal visível. | O resultado registrado por cada camada, sem inferir causalidade remota. |
| Fechamento ou perda da conexão | A desconexão é detectada quando a plataforma permite e os recursos locais são liberados. | Se a desconexão se propaga para a tarefa de backend e para a API externa. |
Verifique os recursos e as consequências antes da implantação
A verificação deve percorrer toda a cadeia, não apenas o código do botão. Confirme se o cliente HTTP local aceita um sinal de cancelamento, se o backend associa esse sinal à tarefa correta e se o mecanismo remoto — caso exista — corresponde ao endpoint e ao tipo de operação utilizados. A capacidade de cancelamento específica de um produto não prova que outra API do mesmo provedor, ou de outro provedor, tenha comportamento equivalente.
Verifique também a limpeza local: tarefas auxiliares, listeners, conexões e referências associadas a uma solicitação não devem permanecer ativas sem uma razão conhecida. Em serviços com várias solicitações simultâneas, garanta que um sinal cancele apenas a operação associada, e não uma tarefa compartilhada por outros usuários. Registre identificadores de operação e estados finais de modo que seja possível reconstruir o que foi solicitado e o que foi confirmado.
Antes de fazer afirmações sobre custos, uso ou processamento remoto, consulte a documentação oficial daquela integração e execute testes limitados que estejam de acordo com os termos. Uma API de chamadas em lote, por exemplo, documenta uma operação de cancelamento para suas chamadas e destinatários; isso é evidência sobre aquele endpoint, não sobre geração de texto. Mantenha essa distinção no projeto, nos testes e na comunicação com o usuário.
Lista de aceitação
Não prometa um alcance de cancelamento maior do que aquele que você consegue verificar.
- 01A interface diferencia cancelamento solicitado, timeout e desconexão.
- 02O sinal chega a cada camada que realmente aceita cancelamento; as limitações das demais camadas estão documentadas.
- 03O backend consegue associar um sinal à operação correta e registrar o resultado observado.
- 04As capacidades do cliente da API externa e da própria API foram verificadas na documentação específica.
- 05Tarefas, conexões e estados locais são verificados após cancelamento, conclusão, expiração do prazo e perda de conexão.
- 06Os testes abrangem cancelamento antes do envio, durante a espera e em uma corrida com a resposta.
- 07A interface não afirma que o provedor interrompeu o trabalho ou alterou o faturamento sem evidências específicas.
Critério final: informe o que você sabe e verifique o que não sabe
O padrão essencial é simples, embora a implementação dependa da arquitetura: registre o motivo, propague o sinal pelas camadas que você controla, aplique uma política determinística de estados e verifique quais recursos continuam ativos. AbortController pode ajudar a interromper a espera no navegador; ele não substitui o cancelamento no backend nem o mecanismo remoto que uma API talvez ofereça.
Antes de integrar um serviço, responda por escrito a estas perguntas: a biblioteca HTTP aceita cancelamento? O backend detecta o abandono? A API externa tem uma operação documentada para interromper uma tarefa aceita? O que é possível observar depois de usá-la? O que se sabe sobre uso e faturamento? Se alguma resposta estiver faltando, registre a incerteza e evite prometer mais do que a integração demonstra.
Cancelar corretamente não significa garantir que todo trabalho desapareça em todas as camadas. Significa manter estados claros, interromper aquilo que o aplicativo consegue interromper, liberar recursos locais, testar as corridas e comunicar honestamente o alcance de cada ação.
Questões em aberto
- As fontes verificadas fornecidas não documentam o comportamento de uma API específica de geração de texto diante do fechamento de uma conexão, de um sinal abortado ou de uma solicitação de cancelamento.
- Não é possível generalizar, a partir dos exemplos de cancelamento de chamadas em lote ou de chamadas de retorno, o comportamento de APIs de geração de IA.
- As fontes não estabelecem se um cancelamento local interrompe o processamento do provedor nem como afeta o uso ou o faturamento; isso deve ser verificado para cada integração.
- O suporte e o resultado do cancelamento em bibliotecas HTTP de backend dependem das implementações escolhidas e não estão especificados nas fontes fornecidas.
Continue a explorar
Fontes consultadas
Correções e transparência
Se encontrar informação incorreta ou desatualizada, envie-nos a página e a fonte que devemos rever.
Propor uma correção