How to interrupt and restart a recursive JavaScript function?

I’m trying to figure out how to stop and restart a recursive function in JavaScript. Here’s what I’m working with:

document.querySelector('body').addEventListener('click', function(e) {
    if (e.target.classList.contains('triggerButton')) {
        console.log(e.target);
        cyclicOperation(1);
    }
});

function cyclicOperation(count) {
    setTimeout(function() {
        document.querySelector('.output').innerHTML += count + ' hello<br>';
        cyclicOperation(count + 1);
    }, 3000);
}

When the triggerButton is clicked, I want the cyclicOperation function to stop if it’s already running and then start over from the beginning. It doesn’t matter what number it starts with. How can I achieve this? I’m not sure about the right approach or terminology to use. Any help would be great. Thanks!

hey there! you could try using a global variable as a flag to control the recursion. smth like:

let isRunning = false;

function cyclicOperation(count) {
  if (!isRunning) return;
  // rest of your code here
}

toggle isRunning when the button’s clicked. this way u can stop/start the function easily!

To interrupt and restart your recursive function, you can implement a combination of a global flag and clearTimeout. Here’s how you might modify your code:

let timer;
let isRunning = false;

document.querySelector('body').addEventListener('click', function(e) {
    if (e.target.classList.contains('triggerButton')) {
        if (isRunning) {
            clearTimeout(timer);
            isRunning = false;
        }
        cyclicOperation(1);
    }
});

function cyclicOperation(count) {
    isRunning = true;
    timer = setTimeout(function() {
        document.querySelector('.output').innerHTML += count + ' hello<br>';
        cyclicOperation(count + 1);
    }, 3000);
}

This approach allows you to stop the current execution and start a new one from the beginning each time the button is clicked. The clearTimeout ensures any pending operations are cancelled before restarting.

I’ve dealt with similar issues in my projects. One effective approach is using a closure to manage the state and control flow. Here’s a pattern that’s worked well for me:

function createCyclicOperation() {
    let timer;
    
    return function(count = 1) {
        clearTimeout(timer);
        
        const output = document.querySelector('.output');
        output.innerHTML += `${count} hello<br>`;
        
        timer = setTimeout(() => this(count + 1), 3000);
    };
}

const cyclicOp = createCyclicOperation();

document.body.addEventListener('click', e => {
    if (e.target.classList.contains('triggerButton')) {
        cyclicOp();
    }
});

This setup gives you more control and encapsulation. The closure keeps track of its own timer, making it easier to manage state between calls. Each click on the trigger button will stop the current cycle and start a new one. It’s clean, efficient, and avoids global variables.