Condições de parada e laços infinitos
Projete condições de término alcançáveis, acompanhe o progresso do estado e diagnostique atualizações que fazem um laço repetir para sempre.

Um laço termina quando a saída é alcançável
Uma condição de parada define quando uma repetição deve acabar. Em laços como while, ela costuma aparecer de forma invertida: o corpo continua enquanto a condição é verdadeira e para quando ela se torna falsa.
ENQUANTO condição de continuação FAÇA
ações
FIM ENQUANTO
Um laço infinito ocorre quando a execução continua sem alcançar uma saída. O erro nem sempre está na expressão da condição. O estado pode não mudar, mudar na direção errada, saltar sobre o valor esperado ou seguir um caminho que ignora a atualização.
Para avaliar se um laço termina, procure três elementos:
- estado observado: quais valores a condição lê;
- saída: qual situação torna a condição falsa ou aciona uma interrupção;
- progresso: o que aproxima o estado dessa situação em toda passagem que repete.
Uma condição legível descreve a porta. O progresso garante que o algoritmo caminhe até ela.
Escreva um contrato de término antes do laço
Antes da sintaxe, responda quatro perguntas:
| Pergunta | Exemplo de resposta |
|---|---|
| O que mantém a repetição ativa? | ainda existem tarefas pendentes |
| Qual estado representa isso? | restante > 0 |
| O que muda a cada passagem? | processar entre 1 e 5 tarefas |
| Por que a saída será alcançada? | restante diminui e não fica abaixo de zero |
Esse é um contrato de término: uma explicação curta que conecta intenção, condição e atualização. Se uma das respostas depende de “provavelmente mudará”, ainda existe uma lacuna.
Considere 23 tarefas, processadas em lotes de até cinco:
restante <- 23
ENQUANTO restante > 0 FAÇA
processadas <- MÍNIMO(5, restante)
restante <- restante - processadas
FIM ENQUANTO
O uso de MÍNIMO(5, restante) evita processar mais tarefas do que existem no último lote. Enquanto o laço estiver ativo, processadas ficará entre 1 e 5; portanto, restante obrigatoriamente diminuirá.
Meça a distância que falta até a saída
Uma forma rigorosa de justificar o término é escolher uma medida de progresso, também chamada de medida de término. Ela deve:
- possuir um limite que impeça redução indefinida;
- diminuir estritamente em toda iteração que continuará o laço.
No exemplo, a medida é o próprio restante. Ela começa em 23, nunca é negativa e diminui pelo menos uma unidade em cada passagem:
23 → 18 → 13 → 8 → 3 → 0
Quando chega a zero, restante > 0 se torna falsa. O argumento não depende de observar que “funcionou uma vez”; ele explica por que qualquer quantidade inicial não negativa termina, desde que todo lote ativo processe ao menos uma tarefa.
Em uma contagem crescente, a variável aumenta, mas a distância até o limite diminui. Para i < limite, uma medida possível é:
limite - i
Se i cresce uma unidade e limite permanece fixo, essa distância diminui até a condição ficar falsa. A documentação do verificador Dafny formaliza a mesma ideia: uma medida de término deve diminuir estritamente e ser limitada.
Essa medida é diferente das invariantes vistas em contadores e acumuladores. A invariante descreve algo que continua verdadeiro; a medida descreve algo que se aproxima do fim.
Compare três rastros antes de corrigir o código
O histórico do estado revela classes diferentes de defeito:
| Rastro | Diagnóstico provável |
|---|---|
23, 18, 13, 8, 3, 0 | progresso em direção à saída |
23, 23, 23, 23... | atualização ausente ou sem efeito |
0, 3, 6, 9, 12... com saída em valor == 10 | atualização salta sobre o alvo e continua se afastando |
No terceiro caso, este laço não termina:
let valor = 0;
while (valor !== 10) {
valor += 3;
}
A sequência pula de 9 para 12. Depois disso, continua crescendo e nunca volta a 10. Trocar a condição por valor < 10 pode ser correto se a regra real for “repetir enquanto estiver abaixo do limite”. Se o resultado precisa ser exatamente 10, mudar apenas o operador esconderia um problema no passo; o contrato do domínio deve decidir a correção.
Investigue as causas por categoria
O estado não muda
let tentativas = 0;
while (tentativas < 3) {
console.log("Tentando novamente");
}
tentativas continua zero. A condição permanece verdadeira porque nenhuma instrução altera o valor que ela observa.
O estado muda na direção oposta
let restante = 5;
while (restante > 0) {
restante += 1;
}
A condição espera que restante chegue a zero ou menos, mas a atualização aumenta a distância. Nomear a medida — aqui, restante — torna a contradição evidente.
A saída depende de uma igualdade que pode ser pulada
Passos maiores que uma unidade, arredondamentos ou valores externos podem nunca produzir o valor exato. Compare a regra do problema antes de trocar != por < ou >.
Apenas alguns caminhos atualizam o estado
Uma estrutura condicional pode criar uma rota que volta ao teste sem avançar. O caso mais discreto costuma envolver continue.
A mudança depende de algo externo
Um laço pode aguardar entrada, mensagem ou resposta que talvez nunca chegue. Nesse cenário, não existe prova de término baseada apenas no estado local. Cancelamento, prazo máximo, encerramento do canal ou outra política explícita precisa fazer parte do contrato do sistema.
Verifique se continue pula a atualização
continue encerra a passagem atual e transfere o fluxo para o próximo teste. Isso pode ignorar instruções colocadas depois dele:
const itens = ["A", null, "B"];
let i = 0;
while (i < itens.length) {
if (itens[i] === null) {
continue;
}
console.log(itens[i]);
i += 1;
}
Quando i vale 1, o item é null. O continue volta à condição sem executar i += 1; a posição permanece 1 para sempre.
Uma correção é garantir o avanço antes de qualquer desvio:
const itens = ["A", null, "B"];
let i = 0;
while (i < itens.length) {
const item = itens[i];
i += 1;
if (item === null) {
continue;
}
console.log(item);
}
Agora a medida itens.length - i diminui em todas as passagens. Um for também pode tornar a atualização estruturalmente mais visível quando a progressão é previsível.
Use break para uma saída legítima, não para esconder o defeito
break encerra imediatamente o laço mais interno. Ele é adequado quando o evento de saída nasce durante o processamento, como localizar um item:
let encontrado = null;
for (const codigo of [14, 27, 31, 42]) {
if (codigo === 31) {
encontrado = codigo;
break;
}
}
Nesse caso existem duas formas de término:
- a coleção acaba;
- o valor procurado é encontrado e
breakexecuta.
Adicionar break a uma condição arbitrária apenas para impedir o travamento pode mascarar uma atualização errada. Para ser parte do contrato, o evento que dispara a interrupção também precisa ser alcançável ou a repetição precisa possuir outra saída.
while (true) não é automaticamente um erro. Ele torna explícito que a saída estará no corpo ou fora do processo:
ENQUANTO verdadeiro FAÇA
mensagem <- RECEBER()
SE mensagem indica encerramento ENTÃO
INTERROMPER
FIM SE
PROCESSAR(mensagem)
FIM ENQUANTO
Serviços, consumidores de mensagens e interfaces orientadas a eventos podem ser deliberadamente contínuos. Ainda assim, precisam de política de cancelamento, tratamento de falhas e encerramento controlado. Um laço intencionalmente duradouro é uma decisão arquitetural; um laço infinito acidental é uma promessa de término quebrada.
Coloque limites operacionais sem confundi-los com correção
Durante a depuração, um limite de iterações evita que um teste defeituoso consuma recursos indefinidamente:
let iteracoes = 0;
const limiteDeDiagnostico = 1000;
while (condicao()) {
iteracoes += 1;
if (iteracoes > limiteDeDiagnostico) {
throw new Error("Laço excedeu o limite de diagnóstico");
}
executarPasso();
}
Esse limite revela um sintoma e protege o ambiente de teste. Ele não demonstra que o algoritmo está correto: entradas válidas talvez exijam mais de mil passagens, ou o erro pode ocorrer antes desse ponto.
Em produção, timeouts, cancelamento e cotas podem ser requisitos legítimos. Seus valores devem vir do comportamento esperado do sistema, não de uma tentativa de compensar uma condição de parada mal definida.
Depure observando somente o estado relevante
Use este roteiro:
- interrompa a execução ou reproduza o caso com um limite seguro;
- escreva a condição em uma frase do domínio;
- liste todas as variáveis lidas pela condição;
- registre seus valores antes e depois de cada passagem;
- marque
break,continue,returne exceções possíveis; - identifique uma medida limitada que deveria se aproximar da saída;
- confira se ela progride em todos os caminhos que repetem.
Uma assinatura de estado é o conjunto dos valores que determina o próximo passo. Se um algoritmo determinístico volta à mesma assinatura, ele repetirá a mesma transição e entrou em um ciclo. No exemplo com continue, a assinatura relevante inclui i = 1 e o mesmo vetor; nada muda entre duas avaliações.
Logs precisam ser seletivos. Registrar apenas “executou” confirma repetição, mas não explica a causa. Registre os valores da condição, a medida de progresso e o caminho escolhido.
Faça um teste de mesa com poucas passagens antes de executar entradas grandes.
Teste as fronteiras e todos os caminhos de retorno
Para um laço que processa tarefas, teste:
- estado inicial que já torna a condição falsa;
- entrada que exige exatamente uma passagem;
- quantidade divisível pelo tamanho do lote;
- último lote menor que a capacidade;
- menor e maior valores válidos;
- caminho que executa
continue; - caminho que executa
break; - atualização zero, negativa ou maior que o alvo, quando puder chegar da entrada.
Não verifique apenas a saída. Confira também o número de iterações, o estado final e a propriedade de progresso. Um erro de limite pode terminar e ainda executar uma vez a mais; término e correção do resultado são obrigações relacionadas, mas diferentes.
Pratique com um estoque a separar
Um depósito possui 17 pedidos pendentes e separa até quatro por rodada.
- escreva o contrato de término em quatro respostas;
- implemente o laço sem deixar o estoque negativo;
- registre
pendentesapós cada rodada; - indique uma medida de progresso e seu limite;
- teste entradas
0,1,4,5e17; - introduza propositalmente atualização ausente, direção invertida e passo zero;
- diagnostique cada defeito apenas pelo rastro do estado.
Depois, acrescente uma regra que ignore uma rodada cancelada. Garanta que esse caminho não impeça o avanço do estado responsável pelo término.
O que você deve guardar
Condição de parada, estado e progresso precisam concordar. Identifique uma saída alcançável e uma medida limitada que avance estritamente em toda passagem; depois teste cada caminho capaz de voltar à condição.
Estados repetidos revelam estagnação ou ciclos, alvos pulados denunciam igualdades frágeis e continue pode esconder uma atualização ausente. break, timeouts e limites são úteis quando fazem parte do contrato, mas não substituem a correção da lógica. Continue estudando como essas responsabilidades se combinam em estruturas de repetição aninhadas.
Referências
- MDN Web Docs — Loops and iteration. Acesso em 11 set. 2026.
- MDN Web Docs —
while. Acesso em 11 set. 2026. - Microsoft Learn — Iteration statements:
for,foreach,doewhile. Acesso em 11 set. 2026. - Dafny Documentation — Loop termination. Acesso em 11 set. 2026.