简体   繁体   English

javascript 停止无限循环

[英]javascript stop an infinite loop

This is node.js.这是 node.js。

I have a function that might become an infinite loop if several conditions are met.我有一个 function,如果满足几个条件,它可能会变成无限循环。 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?如果infiniteloop变量设置为 true,循环就会停止,对吗?
  2. How do I detect the infinite loop?如何检测无限循环? Something that checks every 3 seconds would be good.每 3 秒检查一次的东西会很好。
  3. The infiniteloop variable cannot be changed while it's looping if it's on the same process.如果infiniteloop循环变量在同一进程中,则它在循环时不能更改。 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.谢谢你的帮助。

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. 使用setTimeout恢复循环允许主node.js事件循环处理其他事件,例如w.Stop。

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. 此代码阻止了JavaScript的单线程性质,因此,除非使用Web Worker,否则您将始终锁定所有内容。 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. 最好不要每隔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++;
  }
}

Actually, you don't need to stop a infinite loop. 实际上,您不需要停止无限循环。 use setImmediate 使用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 为什么使用setImmediate

  1. the Memory comsumption kept low, will not cause memory leek; 内存消耗保持在较低水平,不会引起内存韭葱;
  2. will not throw a RangeError: Maximum call stack size exceeded ; 不会抛出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. 而已。

You could create a child process (fork) to check if your actual process is responding. 您可以创建一个子进程(分支)以检查您的实际进程是否正在响应。 Fork would be sending messages to parent if there is no response - kill parent and fork. 如果没有响应,则分叉将向父级发送消息-杀死父级和分叉。 Something similar to this what you could see in this gist: https://gist.github.com/kevinohara80/3173692 您在本要点中可以看到的与此类似的东西: https : //gist.github.com/kevinohara80/3173692

If you would be using express node.js server you could try out middleware harakiri which would do what you need. 如果您要使用Express Node.js服务器,则可以尝试中间件harakiri ,它可以满足您的需求。

I want to present my solution. 我想提出我的解决方案。 In my case I was having several infinite loops ( while(true) ) that are only terminated by break statements. 就我而言,我有几个无限循环( while(true) ),这些无限循环仅由break语句终止。 Whether the conditions for these break statements are ever reached is hard to determine for sure, and the algorithm wasn't developed by myself so a complete refactoring was also not an option. 很难确定是否满足这些break语句的条件,而且该算法不是我自己开发的,因此也不可以选择完全重构。

I like the simple solution by @JasonSebring with a loop counter. 我喜欢@JasonSebring带有循环计数器的简单解决方案。 Just setting a limit for the number of iterations was fine in my case. 在我的情况下,只需为迭代次数设置一个限制就可以了。 But I did not want to insert all these variables into my code, plus I needed a solution that works with nested loops. 但是我不想将所有这些变量都插入到我的代码中,此外,我还需要一个适用于嵌套循环的解决方案。 So I came up with this wrapper function: 所以我想出了这个包装函数:

/**
 * 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.`);
      }
    }
  }
}

Usage is like this: 用法是这样的:

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

If your loop had a condition before, you will have to check this condition inside the arrow function and use return true; 如果循环之前有条件,则必须在arrow函数中检查此条件,并使用return true; like in the example above. 就像上面的例子一样。

Since the loop counter is not exposed it is possible to nest this solution without the need to find different variable names for the counters of inner loops. 由于不公开循环计数器,因此可以嵌套此解决方案,而无需为内部循环的计数器找到不同的变量名。

In a function, simply return false or undefined.在 function 中,只需返回 false 或 undefined。

Manually throw new Error("ERROR") in a function.在 function 中手动抛出 new Error("ERROR")。

Set a function to run on a timer – var timer = setInterval(FUNCTION, 1000).将 function 设置为在计时器上运行 - var timer = setInterval(FUNCTION, 1000)。 Then clear it to stop – clearInterval(timer)然后清除它停止 - clearInterval(timer)

Run the script with workers that can be terminated.使用可以终止的工作人员运行脚本。

Use window.stop() to prevent the page from loading and running.使用 window.stop() 来阻止页面加载和运行。

For NodeJS only – Use process.abort() or process.exit().仅适用于 NodeJS – 使用 process.abort() 或 process.exit()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM