javascript stop an infinite loop

10,674

Solution 1

A solution based on a mix of the other proposals:

function Worker()
{
    this.MaxIterations = 1000000;
    this.Enabled = true;    
    this.condition = true;
    this.iteration = 0;
    this.Loop = function()
    {
        if (this.condition 
            && this.Enabled 
            && this.iteration++ < this.MaxIterations)
        {
            console.log(this.iteration);
            setTimeout(this.Loop.bind(this),0);
        }
    };  
    this.Stop = function()
    {
        this.Enabled = false;
    };
}
var w = new Worker();
setTimeout(w.Loop.bind(w), 0);
setTimeout(w.Stop.bind(w), 3000);

Not sure this is optimal, but that should work as expected.

The use of setTimeout to resume the loop allows the main node.js event loop to process other events, such a w.Stop.

Solution 2

Actually, you don't need to stop a infinite loop. use setImmediate

for instance:

var immediateId;

function loop () {
    console.log('hi');
    immediateId = setImmediate(loop);
}

loop();

This chunk of code will keep saying hi, until you stop it.

//stop the loop:
clearImmediate(immediateId);

why using setImmediate

  1. the Memory comsumption kept low, will not cause memory leek;
  2. will not throw a RangeError: Maximum call stack size exceeded;
  3. the performance is good;

Further more,

I created this module for easily managing infinite loop:

var util = require('util');
var ee = require('events').EventEmitter;

var Forever = function() {
    ee.call(this);
    this.args = [];
};

util.inherits(Forever, ee);

module.exports = Forever;

Forever.prototype.add = function() {
    if ('function' === typeof arguments[0]) {
        this.handler = arguments[0];
        var args = Array.prototype.slice.call(arguments, 1);
        if (args.length > 0) {
            this.args = args;
        }
    } else {
        this.emit('error', new Error('when using add function, the first argument should be a function'));
        return 0;
    }
    return this;
};

Forever.prototype.run = function() {
    var handler = this.handler;
    var args = this.args;
    var that = this;

this._immediateId = setImmediate(function() {
        if (typeof handler === 'function') {

            switch (args.length) {
                // fast cases
                case 0:
                    handler.call(that);
                    that.run();
                    break;
                case 1:
                    handler.call(that, args[0]);
                    that.run();
                    break;
                case 2:
                    handler.call(that, args[0], args[1]);
                    that.run();
                    break;
                    // slower
                default:
                    handler.apply(that, args);
                    that.run();
            }
                } else {
                //no function added
                that.emit('error', new Error('no function has been added to Forever'));
            }
        });
};

Forever.prototype.stop = function() {
    if (this._immediateId !== null) {
        clearImmediate(this._immediateId);
    } else {
        this.emit('error', new Error('You cannot stop a loop before it has been started'));
    }
};

Forever.prototype.onError = function(errHandler) {
    if ('function' === typeof errHandler) {
        this.on('error', errHandler);
    } else {
        this.emit('error', new Error('You should use a function to handle the error'));
    }
    return this;
};

example usage:

var Forever = require('path/to/this/file');
var f = new Forever();

// function to be runned
function say(content1, content2){
    console.log(content1 + content2);
}

//add function to the loop
//the first argument is the function, the rest are its arguments
//chainable api
f.add(say, 'hello', ' world!').run();

//stop it after 5s
setTimeout(function(){
    f.stop();
}, 5000);

That's it.

Solution 3

Infinity in this case is up to you what the max iterations of a loop will be. This code is blocking the single threaded nature of JavaScript so you will lock up everything anyway unless you are using web workers. Better to not check it every x seconds because this code will block execution of an interval or timeout anyway, rather have it within the loop itself as a max threshold of loop iterations.

var infiniteloop = false;
var condition = true;
var loopCounter = 1;
var maxLoopIterations = 1000; 
function loop () {
  while (condition) {
    console.log('hi');
    infiniteLoop = (loopCounter >= maxLoopIterations); 
    if (infiniteloop) {
      condition = false;
      console.log('oh last one');
      break;
    }
    loopCounter++;
  }
}
Share:
10,674
Harry
Author by

Harry

Updated on June 17, 2022

Comments

  • Harry
    Harry almost 2 years

    This is node.js.

    I have a function that might become an infinite loop if several conditions are met. Untrusted users set these conditions so for the purpose of this question please assume the infinite loop is unfixable.

    Still I need a way to stop the infinite loop.

    Here is some sample code for what i'm trying to do:

    var infiniteloop = false;
    var condition = true
    function loop () {
      while (condition) {
        console.log('hi')
        if (infiniteloop) {
          condition = false
          console.log('oh last one')
        }
      }
    }
    
    loop()
    

    So a few questions based on what I'm trying to do.

    1. If the infiniteloop variable is set to true, the loop will stop right?
    2. How do I detect the infinite loop? Something that checks every 3 seconds would be good.
    3. The infiniteloop variable cannot be changed while it's looping if it's on the same process. I have to store the variable in a different process?
    4. Whatever detects the infinite loop needs to live in a different process? Ideally same process would be nice but whatever works?

    Thanks for your help.