阿里云主机折上折
  • 微信号
Current Site:Index > Infinite loops and recursion ("users will close the page anyway")

Infinite loops and recursion ("users will close the page anyway")

Author:Chuan Chen 阅读数:13213人阅读 分类: 前端综合

Infinite Loops and Recursion ("Users Will Close the Tab Anyway")

In front-end development, defensive programming typically refers to writing robust, maintainable code. But what if we do the opposite? How can we write code that frustrates colleagues and drives users to despair? Infinite loops and recursion are perfect tools—especially when cleverly hidden within seemingly harmless logic.

1. The Art of Infinite Loops

Infinite loops are the most straightforward "defensive measure" in front-end code. They can freeze the page, spike CPU usage, or even trigger browser crashes. Here are some classic implementations:

1.1 Elegant Variations of while(true)

function freezePage() {
    while (1) {
        console.log("Users will close the tab, don't worry");
    }
}

A more subtle approach is using expressions that always evaluate to true:

let i = 0;
while (i < 10) {
    console.log("Look, I have a termination condition!");
    i--; // Secretly ensuring i never reaches 10
}

1.2 Loop Traps in Event Listeners

Embedding infinite loops in event callbacks ensures users can no longer interact once they trigger an action:

document.getElementById("submit").addEventListener("click", () => {
    while (true) {
        alert("Submitting... Please wait patiently");
    }
});

1.3 Self-Replicating Timers

Using setInterval or setTimeout to create infinitely recursive timed tasks:

function recursiveTimeout() {
    setTimeout(() => {
        console.log("This is just a normal log");
        recursiveTimeout(); // Self-invocation
    }, 0);
}
recursiveTimeout();

2. The Abyss of Recursion

Recursion itself is a legitimate programming technique, but when used well (or poorly), it can easily overwhelm the call stack.

2.1 Recursion Without a Termination Condition

function crashBrowser() {
    crashBrowser(); // Direct self-call
}

2.2 Disguised "Safe" Recursion

A seemingly reasonable termination condition that is never actually met:

function fakeSafeRecursion(n) {
    if (n > 100) return; // Appears to have a termination condition
    fakeSafeRecursion(n - 1); // But n keeps decreasing
}
fakeSafeRecursion(1);

2.3 Indirect Recursion: A Calls B, B Calls A

function functionA() {
    functionB();
}
function functionB() {
    functionA();
}
functionA(); // Mutual calls, endless cycle

3. The Ultimate Weapon: Combining Asynchrony

Asynchronous code can make infinite loops and recursion even harder to detect, especially under the guise of Promises and async/await:

3.1 Infinite Promise Chains

function infinitePromiseChain() {
    return Promise.resolve().then(() => {
        console.log("This is just a normal async task");
        return infinitePromiseChain(); // Chained self-invocation
    });
}
infinitePromiseChain();

3.2 Hidden Recursion with async/await

async function infiniteAsync() {
    await Promise.resolve();
    infiniteAsync(); // Looks like a normal async function
}
infiniteAsync();

4. How to Make Debugging Even Harder

To ensure colleagues give up on fixing it, combine the following techniques:

4.1 Dynamically Generated Termination Conditions

function dynamicConditionCrash() {
    if (Math.random() > 1) return; // Theoretically possible, practically never
    dynamicConditionCrash();
}
dynamicConditionCrash();

4.2 Recursion + Closures + Side Effects

function createRecursiveClosure() {
    let count = 0;
    return function() {
        count++;
        if (count % 1000 === 0) {
            console.log(`Looks like I'm counting: ${count}`);
        }
        createRecursiveClosure()(); // Closure + recursion
    };
}
createRecursiveClosure()();

5. Will Users Really Close the Tab?

Theoretically, users can escape by:

  • Force-closing the browser tab
  • Killing the browser process
  • Pulling the power plug

But as "excellent" front-end developers, we can further prevent these actions:

5.1 Intercepting the beforeunload Event

window.addEventListener("beforeunload", (e) => {
    e.preventDefault();
    e.returnValue = "Are you sure you want to leave?";
    return "Are you sure you want to leave?";
});

5.2 Infinite Popups to Block Closing

window.onbeforeunload = () => {
    while (true) {
        alert("Please stay!");
    }
};

6. The Ultimate Challenge: Making Code Look Normal but Actually Crash

The most "advanced" approach is to trigger crashes under specific conditions, such as:

function sneakyCrash(shouldCrash) {
    if (shouldCrash) {
        sneakyCrash(true); // Recursive crash
    } else {
        console.log("Normal logic");
    }
}
sneakyCrash(false); // Seems safe
sneakyCrash(true);  // Actually crashes

Or using "legitimate" operations to indirectly cause infinite loops, like:

const array = [];
array.push(array); // Self-referential array
console.log(array); // Attempting to print triggers recursive expansion

7. Why This Code Survives Code Review

  • Appears to have termination conditions (but they're never met)
  • Hidden in asynchronous logic (hard to step through)
  • Relies on dynamic data ("only reproduces in production")
  • Well-formatted code (clean syntax, clear variable names)

Example:

// An elegant crash function
function fetchDataWithRetry(maxRetries) {
    if (maxRetries <= 0) return; // Seems reasonable
    fetchDataWithRetry(maxRetries + 1); // Actually grows infinitely
}

8. How to "Fix" Such Code

(Of course, "fix" here means making it harder to detect)

  • Add randomness: if (Math.random() > 0.9999) return;
  • Depend on external state: if (window.userIsAngry) return;
  • Disguise as performance optimization: requestAnimationFrame(recursiveRender);
function recursiveRender() {
    renderFrame();
    requestAnimationFrame(recursiveRender); // Mask recursion with rAF
}

9. Real-World "Best Practices"

Certain library or framework abuses can achieve similar effects, such as:

  • React's infinite rendering:

    function Component() {
        const [state, setState] = useState(0);
        setState(state + 1); // Triggers update on every render
        return <div>{state}</div>;
    }
    
  • Vue's watch loop:

    new Vue({
        data: { count: 0 },
        watch: {
            count(newVal) {
                this.count = newVal + 1; // Watches its own changes
            }
        }
    });
    

10. Philosophical Reflections on Loops and Recursion

If a piece of code never stops, is it still a "program"? Or has it transcended programming to become a natural phenomenon—like the expansion of the universe, until the browser crashes into heat death?

function philosophicalCrash() {
    console.log("I think, therefore I crash");
    philosophicalCrash();
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.