while and do…while

โ–ถ Try It Yourself

The while loop is used when you do not know in advance how many iterations are needed โ€” you loop until a condition changes. The do...while loop is a variant that guarantees the body runs at least once before checking the condition. These loops are essential for event-driven patterns, retry logic, polling, and consuming streams of data. In this lesson you will master both, learn how to prevent infinite loops, and build practical patterns like retry mechanisms and queue processors.

while vs do…while vs for

Loop Condition Checked Minimum Runs Best For
for Before each iteration 0 Known iteration count
while Before each iteration 0 Unknown count โ€” condition may be false from the start
do...while After each iteration 1 โ€” always runs at least once Must execute once before checking โ€” prompts, retries

while Loop Anatomy

Part Location Purpose
Condition Before body Evaluated before each iteration โ€” loop exits when false
Body Inside { } Runs while condition is true
Update Inside body (manual) Must change something that eventually makes condition false
Safety counter Inside body Optional ceiling to prevent infinite loops during development

Common while Patterns

Pattern Condition Example Use Case
Drain a queue queue.length > 0 Process all items until queue is empty
Retry with limit attempts < MAX && !success API call retries with backoff
Read until sentinel line !== 'EXIT' Input processing until stop signal
Converge to value Math.abs(diff) > tolerance Numerical approximation algorithms
Validate input loop !isValid do…while โ€” keep prompting until valid
Note: The key difference between while and do...while: if the condition is false from the very beginning, a while loop runs zero times, but a do...while loop always runs at least once. Use do...while when the first execution is unconditional โ€” for example, showing a prompt before knowing if the input is valid.
Tip: Always add a maximum iteration guard to while loops during development: let safety = 0; while (condition && safety++ < 1000). If your condition variable has a bug and never becomes false, this cap prevents a browser freeze or server crash. Remove or increase it for production loops that legitimately need more iterations.
Warning: The most common infinite loop bug is forgetting to update the condition variable inside the loop body. If while (isRunning) never sets isRunning = false, or while (i < 10) never increments i, the loop runs forever. Every while loop must have a clear path to making its condition false.

Basic Example

// โ”€โ”€ Basic while loop โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
let countdown = 5;
while (countdown > 0) {
    console.log(`T-minus ${countdown}...`);
    countdown--;   // update: will eventually make countdown > 0 false
}
console.log('Liftoff! ๐Ÿš€');

// โ”€โ”€ while to drain a queue โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const taskQueue = ['send-email', 'resize-image', 'generate-report', 'notify-user'];

console.log('
Processing queue:');
while (taskQueue.length > 0) {
    const task = taskQueue.shift();  // remove first item
    console.log(`  Processing: ${task}`);
}
console.log('Queue empty.');

// โ”€โ”€ do...while โ€” always runs at least once โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Simulating user input validation (in a real app this would use a prompt)
const inputs = ['', '  ', 'Alice'];  // simulate three attempts
let inputIndex = 0;
let validName;

do {
    const raw = inputs[inputIndex++] ?? '';
    validName = raw.trim();
    if (!validName) console.log('Name cannot be empty โ€” try again.');
} while (!validName && inputIndex < inputs.length);

console.log(`
Valid name entered: "${validName}"`);

// โ”€โ”€ while with break โ€” search until found โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function findFirstEven(numbers) {
    let i = 0;
    while (i < numbers.length) {
        if (numbers[i] % 2 === 0) {
            return numbers[i];  // return acts like break + value
        }
        i++;
    }
    return null;
}

console.log(findFirstEven([7, 3, 11, 8, 5]));   // 8
console.log(findFirstEven([1, 3, 5, 7]));        // null

// โ”€โ”€ Retry pattern โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function simulateApiCall(attempt) {
    // Simulates a flaky API that fails the first 2 times
    if (attempt < 3) throw new Error(`Network error on attempt ${attempt}`);
    return { data: 'Success!', attempt };
}

function fetchWithRetry(maxAttempts = 5) {
    let attempt  = 1;
    let lastError = null;

    while (attempt <= maxAttempts) {
        try {
            const result = simulateApiCall(attempt);
            console.log(`
Succeeded on attempt ${attempt}:`, result.data);
            return result;
        } catch (err) {
            lastError = err;
            console.log(`Attempt ${attempt} failed: ${err.message}`);
            attempt++;
        }
    }

    throw new Error(`All ${maxAttempts} attempts failed. Last error: ${lastError.message}`);
}

fetchWithRetry();
// Attempt 1 failed: Network error on attempt 1
// Attempt 2 failed: Network error on attempt 2
// Succeeded on attempt 3: Success!

How It Works

Step 1 โ€” while Checks Before Every Iteration

Before the body runs each time, JavaScript evaluates the condition. If it is truthy, the body runs. If it is falsy, the loop exits. This means if countdown starts at 0, the body never runs at all โ€” the condition fails on the first check.

Step 2 โ€” do…while Checks After the First Run

In the validation example, the first prompt always happens โ€” then the condition !validName is checked. If the name is still empty, it loops again. This is the natural model for “ask at least once, then keep asking if needed.”

Step 3 โ€” Array.shift() as Loop Terminator

taskQueue.shift() removes and returns the first element. Each iteration shrinks the array by one. When the array is empty, taskQueue.length > 0 becomes false and the loop exits naturally. This draining pattern is common for processing job queues and event queues.

Step 4 โ€” try/catch Inside while for Retry Logic

Wrapping the operation in try/catch inside the while loop catches errors without exiting the loop. The attempt counter increments in the catch block. When the operation succeeds, return exits the function entirely. When all attempts fail, the code after the while loop throws with the last error.

Step 5 โ€” Always Have a Termination Path

Every while loop needs at least one of: a variable that changes towards making the condition false, a break inside the body, or a return inside the body. The retry example has all three: attempt++ moves toward maxAttempts, return result exits on success, and throwing after the loop handles total failure.

Real-World Example: Pagination and Rate Limiting

// paginated-fetch.js โ€” fetch all pages of a paginated API

async function fetchAllPages(baseUrl, maxPages = 20) {
    const allItems  = [];
    let   page      = 1;
    let   hasMore   = true;

    while (hasMore && page <= maxPages) {
        console.log(`Fetching page ${page}...`);

        const response = await fetch(`${baseUrl}?page=${page}&per_page=20`);
        if (!response.ok) {
            throw new Error(`HTTP ${response.status} on page ${page}`);
        }

        const data = await response.json();
        allItems.push(...data.items);

        // API signals last page via a flag or empty next page
        hasMore = data.hasNextPage ?? data.items.length === 20;
        page++;
    }

    console.log(`Fetched ${allItems.length} total items across ${page - 1} pages`);
    return allItems;
}

// Rate-limited queue processor
async function processWithRateLimit(items, processFn, requestsPerSecond = 5) {
    const delayMs = 1000 / requestsPerSecond;
    let   i       = 0;

    while (i < items.length) {
        const start = Date.now();
        await processFn(items[i]);
        i++;

        // Throttle: wait for the remainder of the time slot
        const elapsed = Date.now() - start;
        const wait    = Math.max(0, delayMs - elapsed);
        if (wait > 0) await new Promise(r => setTimeout(r, wait));
    }
}

Common Mistakes

Mistake 1 โ€” Infinite loop โ€” condition never becomes false

โŒ Wrong โ€” i is never incremented so condition stays true forever:

let i = 0;
while (i < 5) {
    console.log(i);
    // forgot i++ โ€” infinite loop, browser tab freezes
}

โœ… Correct โ€” always update the condition variable:

let i = 0;
while (i < 5) {
    console.log(i);
    i++;  // moves toward termination
}

Mistake 2 โ€” Using while when for is clearer

โŒ Wrong โ€” while loop for a known count is harder to read:

let i = 0;
while (i < items.length) {
    process(items[i]);
    i++;
}

โœ… Correct โ€” for loop when count is known:

for (let i = 0; i < items.length; i++) {
    process(items[i]);
}
// Or even cleaner:
for (const item of items) { process(item); }

Mistake 3 โ€” Skipping do…while when first run is unconditional

โŒ Awkward โ€” duplicated code to run once before the loop:

let input = prompt('Enter name:');
while (!input?.trim()) {
    input = prompt('Enter name:');   // duplicated prompt call
}

โœ… Correct โ€” do…while expresses “ask at least once” naturally:

let input;
do {
    input = prompt('Enter name:');
} while (!input?.trim());

▶ Try It Yourself

Quick Reference

Loop Syntax Minimum Executions
while while (condition) { update; } 0 โ€” condition checked first
do…while do { update; } while (condition); 1 โ€” always runs once
Drain queue while (queue.length > 0) { queue.shift(); } 0 if empty
Retry while (attempt <= max) { try/catch; attempt++; } 1
Safety cap while (cond && safety++ < 1000) 0 โ€” dev guard

🧠 Test Yourself

When is do...while the better choice over a regular while loop?





โ–ถ Try It Yourself