5

这是 node.js。

如果满足多个条件,我有一个函数可能会变成无限循环。不受信任的用户设置了这些条件,因此出于此问题的目的,请假设无限循环是无法修复的。

我仍然需要一种方法来停止无限循环。

这是我正在尝试做的一些示例代码:

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

loop()

所以有几个问题基于我正在尝试做的事情。

  1. 如果infiniteloop变量设置为true,循环将停止对吗?
  2. 如何检测无限循环?每 3 秒检查一次会很好。
  3. 如果infiniteloop变量在同一进程上,则在循环时无法更改变量。我必须将变量存储在不同的进程中吗?
  4. 无论检测到无限循环是否需要存在于不同的进程中?理想情况下,相同的过程会很好,但有什么用?

谢谢你的帮助。

4

6 回答 6

5

基于其他提议的混合解决方案:

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);

不确定这是最优的,但应该可以按预期工作。

使用 setTimeout 来恢复循环允许主 node.js 事件循环处理其他事件,例如 w.Stop。

于 2012-07-13T21:47:39.507 回答
2

在这种情况下,无穷大取决于您循环的最大迭代次数。这段代码阻塞了 JavaScript 的单线程特性,所以除非你使用网络工作者,否则无论如何你都会锁定一切。最好不要每 x 秒检查一次,因为此代码无论如何都会阻止执行间隔或超时,而是将其作为循环迭代的最大阈值放在循环本身内。

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++;
  }
}
于 2012-07-13T18:33:28.240 回答
2

实际上,您不需要停止无限循环。采用setImmediate

例如:

var immediateId;

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

loop();

这段代码会一直说你好,直到你停止它。

//stop the loop:
clearImmediate(immediateId);

为什么使用setImmediate

  1. 内存消耗保持在较低水平,不会造成内存溢出;
  2. 不会抛出RangeError: Maximum call stack size exceeded
  3. 性能好;

此外,

我创建了这个模块来轻松管理无限循环:

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;
};

示例用法:

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);

就是这样。

于 2013-05-27T06:43:14.897 回答
0

您可以创建一个子进程(fork)来检查您的实际进程是否正在响应。如果没有响应,fork 将向父级发送消息 - 杀死父级并分叉。类似于您在此要点中可以看到的内容:https ://gist.github.com/kevinohara80/3173692

如果您要使用 express node.js 服务器,您可以尝试中间件harakiri,它可以满足您的需求。

于 2018-09-28T07:34:16.017 回答
0

我想介绍我的解决方案。在我的例子中,我有几个无限循环(while(true)),它们只被break语句终止。break很难确定是否达到了这些语句的条件,并且该算法不是我自己开发的,因此完全重构也不是一种选择。

我喜欢@JasonSebring 提供的带有循环计数器的简单解决方案。在我的情况下,只需设置迭代次数的限制就可以了。但是我不想将所有这些变量都插入到我的代码中,而且我需要一个适用于嵌套循环的解决方案。所以我想出了这个包装函数:

/**
 * Stop potential infinite loops after a certain number of iterations.
 * @param loopLimit - The maximum number of iterations before loop is aborted.
 * @param silentStop - Whether to abort the loop silently or throw an error instead.
 * @param callBack - Function representing the inner code of the loop.
 */
static finiteLoopHelper(loopLimit, silentStop, callBack) {
  let loopCounter = 0;
  let stopLoop = false;

  while (!stopLoop) {
    // Return value from the callback can invoke an early stop, like a break statement.
    stopLoop = callBack();

    loopCounter++;
    if (loopCounter >= loopLimit) {
      stopLoop = true;
      if (!silentStop) {
        throw Error(`Loop aborted after ${loopLimit} iterations.`);
      }
    }
  }
}

用法是这样的:

let someVariable = 0;

finiteLoopHelper(1000, false, () => { // this line replaces "while (true) {"
  someVariable = someCalculation();
  if (someVariable === someCondition) {
    return false; // like continue in a normal loop.
  }
  codeNotExecutedInCaseOfContinue();

  if (someVariable === someOtherCondition) {
    return true; // like break in a normal loop.
  }

  // Return value at the end can be omitted, because without it the function
  // will return undefined which also keeps the loop running.
  // return false;
});

如果您的循环之前有条件,则必须在箭头函数内检查此条件并return true;像上面的示例一样使用。

由于没有暴露循环计数器,因此可以嵌套此解决方案,而无需为内部循环的计数器找到不同的变量名称。

于 2019-09-27T09:14:35.337 回答
0

在函数中,只需返回 false 或 undefined。

在函数中手动抛出新的错误(“错误”)。

设置一个在定时器上运行的函数——var timer = setInterval(FUNCTION, 1000)。然后清除它停止 - clearInterval(timer)

使用可以终止的工作人员运行脚本。

使用 window.stop() 来阻止页面加载和运行。

仅适用于 NodeJS – 使用 process.abort() 或 process.exit()。

于 2022-02-19T23:20:39.963 回答