Os laços while e do...while são estruturas de repetição fundamentais em JavaScript que permitem executar um bloco de código enquanto uma condição especificada for verdadeira. Diferentemente do laço for, que é mais adequado quando sabemos o número exato de iterações, os laços while e do...while são ideais para situações em que a repetição depende de uma condição que pode mudar durante a execução, como ler dados de uma entrada até que um valor específico seja fornecido.

Embora ambos sejam baseados em condições booleanas, eles diferem no momento em que a condição é avaliada: while verifica a condição antes de executar o bloco, enquanto do...while garante que o bloco seja executado pelo menos uma vez, pois a condição é verificada após a primeira iteração. Compreender essas diferenças é crucial para escrever loops corretos e evitar erros comuns, como loops infinitos.

Diferenças

A principal diferença entre while e do...while está no momento da avaliação da condição de continuação. No laço while, a condição é testada antes de cada iteração. Se a condição for falsa desde o início, o bloco de código nunca será executado. Já no laço do...while, a condição é testada após a execução do bloco, garantindo que o bloco seja executado pelo menos uma vez, independentemente da condição.

Essa diferença é especialmente útil quando você precisa executar uma ação antes de verificar se deve continuar. Por exemplo, ao solicitar entrada do usuário, você pode usar do...while para exibir o prompt pelo menos uma vez e depois repetir enquanto a entrada não for válida. Veja a sintaxe de cada um:

// while: condição testada antes de cada iteração
while (condicao) {
  // bloco de código
}

// do...while: condição testada após cada iteração
do {
  // bloco de código
} while (condicao);

No exemplo abaixo, o laço while não executa nenhuma vez se contador for 10, enquanto o do...while executa uma vez e depois para:

let contador = 10;

while (contador < 10) {
  console.log("while: " + contador); // nunca executado
  contador++;
}

do {
  console.log("do...while: " + contador); // executado uma vez (10)
  contador++;
} while (contador < 10);

Condições de parada

A condição de parada é a expressão booleana que determina se o laço continua ou termina. Em ambos os laços, a condição é avaliada a cada iteração. Se a condição retornar true, o bloco é executado novamente; se retornar false, o laço termina e a execução continua na próxima instrução após o laço.

É essencial que a condição de parada seja eventualmente falsa, caso contrário o laço se tornará infinito. A condição geralmente depende de variáveis que são modificadas dentro do bloco. Por exemplo, um contador que é incrementado até atingir um limite, ou uma flag que muda de valor quando uma condição é satisfeita. Veja um exemplo com while que imprime números de 0 a 4:

let i = 0;
while (i < 5) {
  console.log(i);
  i++;
}
// Saída: 0, 1, 2, 3, 4

No do...while, a condição é verificada após a execução, então o bloco sempre executa pelo menos uma vez. Isso é útil quando você precisa executar uma ação antes de verificar se deve continuar. Por exemplo, ler um número do usuário até que ele digite um valor positivo:

let numero;
do {
  numero = parseInt(prompt("Digite um número positivo:"));
} while (numero <= 0);
console.log("Número válido: " + numero);

Note que a condição de parada deve ser cuidadosamente projetada para evitar loops infinitos. Sempre verifique se a variável da condição está sendo atualizada de forma que eventualmente torne a condição falsa.

Loops infinitos (e como evitar)

Um loop infinito ocorre quando a condição de parada nunca se torna falsa, fazendo com que o laço execute indefinidamente. Isso pode travar o navegador ou o ambiente de execução. Loops infinitos geralmente são causados por erros lógicos, como esquecer de atualizar a variável de controle, usar uma condição que sempre é verdadeira, ou modificar a variável de forma incorreta.

Exemplo clássico de loop infinito com while:

let i = 0;
while (i < 5) {
  console.log(i);
  // esqueci de incrementar i
}
// i nunca muda, então a condição i < 5 é sempre verdadeira

Para evitar loops infinitos, siga estas boas práticas:

  • Atualize a variável de controle: Certifique-se de que a variável usada na condição é modificada dentro do bloco, de modo que a condição possa se tornar falsa.
  • Use condições simples e verificáveis: Evite condições complexas que possam ser sempre verdadeiras devido a um erro lógico.
  • Adicione um limite de segurança: Em loops que dependem de entrada externa, considere adicionar um contador máximo de iterações para interromper o loop se ele exceder um número razoável.
  • Teste o loop com valores de fronteira: Verifique se o loop se comporta corretamente quando a condição é inicialmente falsa (no caso do while) ou quando a condição se torna falsa após a primeira iteração.

Aqui está um exemplo de loop while com um contador de segurança:

let tentativas = 0;
const maxTentativas = 10;
let senhaCorreta = false;

while (!senhaCorreta && tentativas < maxTentativas) {
  let entrada = prompt("Digite a senha:");
  if (entrada === "1234") {
    senhaCorreta = true;
    console.log("Senha correta!");
  } else {
    tentativas++;
    console.log("Senha incorreta. Tentativa " + tentativas);
  }
}

if (!senhaCorreta) {
  console.log("Número máximo de tentativas excedido.");
}

No do...while, o mesmo cuidado se aplica: a condição deve eventualmente se tornar falsa. Como o bloco executa pelo menos uma vez, é ainda mais importante garantir que a primeira execução não cause um estado que impeça a condição de se tornar falsa.

Boas práticas e observações finais

Ao usar laços while e do...while, prefira while quando não houver necessidade de executar o bloco pelo menos uma vez. Use do...while quando a primeira execução for necessária, como em validações de entrada. Sempre inicialize as variáveis de controle antes do laço e atualize-as dentro do bloco. Evite modificar a variável de controle dentro da condição, pois isso pode tornar o código confuso. Por fim, lembre-se de que loops infinitos podem ser interrompidos com break (embora o uso excessivo de break possa indicar que um laço while não é a melhor escolha).

Referências

Exercícios

  1. Escreva um laço while que imprima os números de 1 a 10 no console.

    ✓ Resposta:
    let i = 1;
    while (i <= 10) {
      console.log(i);
      i++;
    }
  2. Escreva um laço do...while que solicite ao usuário um número maior que 10, repetindo até que ele digite um número válido. Use prompt e parseInt.

    ✓ Resposta:
    let numero;
    do {
      numero = parseInt(prompt("Digite um número maior que 10:"));
    } while (numero <= 10);
    console.log("Número válido: " + numero);
  3. O que acontece se a condição de um laço while for falsa desde o início? E no do...while?

    ✓ Resposta:

    No while, o bloco nunca é executado. No do...while, o bloco é executado uma vez e depois a condição é testada; como é falsa, o laço termina.

  4. Escreva um laço while que percorra um array de números e imprima apenas os números pares. Use o array: [3, 8, 12, 5, 20, 7].

    ✓ Resposta:
    let numeros = [3, 8, 12, 5, 20, 7];
    let i = 0;
    while (i < numeros.length) {
      if (numeros[i] % 2 === 0) {
        console.log(numeros[i]);
      }
      i++;
    }
    // Saída: 8, 12, 20
  5. Corrija o loop infinito abaixo: let x = 0; while (x < 5) { console.log(x); }

    ✓ Resposta:

    Falta incrementar x dentro do loop. A versão correta é:

    let x = 0;
    while (x < 5) {
      console.log(x);
      x++;
    }