如何让程序等待javascript中的变量更改?


69

我想强制JavaScript程序在其执行的某些特定点等待,直到变量已更改。有办法吗?我已经找到了一个称为“叙述性JavaScript”的扩展,该扩展迫使程序等待事件发生。有没有办法创建一个新事件,例如一个类似于onclick事件的“变量更改事件”。


我认为最好的方法是使用rxjs库并将observable用作变量。
Az264 '19

Answers:


91

编辑2018:请研究对象获取器,设置器代理。下面的旧答案:


一个快速简便的解决方案是这样的:

var something=999;
var something_cachedValue=something;

function doStuff() {
    if(something===something_cachedValue) {//we want it to match
        setTimeout(doStuff, 50);//wait 50 millisecnds then recheck
        return;
    }
    something_cachedValue=something;
    //real action
}

doStuff();

1
现在进行编辑,它每隔50毫秒检查一次,并检查是否存在非布尔值。正如我所说的那样,快速便捷的方法是,请访问以下链接:stackoverflow.com/questions/1029241/…(已由@mplungjan发布),以寻求更深入的解决问题的方法。
aularon'9

我想你的意思是something===something_cachedValue。然后它起作用了:jsfiddle.net/pEnYC
Skilldrick 2010年

@Skilldrick,是的,我的意思是,我修复了它。感谢您的纠正:))
aularon 2010年

1
谢谢大家的答案!但是在所有解决方案中,程序都不会阻塞!它将正常继续执行,如果变量在执行过程中发生更改,则将调用回调。但是,我要执行的是强制程序阻止其执行,直到变量发生更改(通过调用函数),然后从被阻止的地方继续!有任何想法吗?
Thanasis Petsas 2010年

像这个家伙一样,使变量触发所需的方法。这样,该方法将不会等待任何东西,在变量准备好时将被调用。jsfiddle.net/cDJsB
JakeJ

24

JavaScript解释器是单线程的,因此当代码在其他不会更改变量的代码中等待时,变量永远不会更改。

我认为将变量包装在具有getter和setter函数的某种对象中是最好的解决方案。然后,可以在调用对象的setter函数时在调用的对象中注册一个回调函数。然后,您可以在回调中使用getter函数来检索当前值:

function Wrapper(callback) {
    var value;
    this.set = function(v) {
        value = v;
        callback(this);
    }
    this.get = function() {
        return value;
    }  
}

可以像这样容易使用:

<html>
<head>
<script type="text/javascript" src="wrapper.js"></script>
<script type="text/javascript">
function callback(wrapper) {
    alert("Value is now: " + wrapper.get());
}

wrapper = new Wrapper(callback);
</script>
</head>
<body>
    <input type="text" onchange="wrapper.set(this.value)"/>
</body>
</html>

2
如果您可以控制原始变量,可能是最好的解决方案。
Skilldrick 2010年

谢谢您的回答,但这并不是我想要的。请看我对aularon的评论!
Thanasis Petsas 2010年

1
正如我所解释的,JavaScript解释器是单线程的。您不能阻塞该线程并使其等待其他线程更改变量,因为没有其他线程可以更改变量。当另一个JavaScript事件处理程序已在执行时,则无法执行。
重新启动

它按所述方式工作。谢谢!!!这非常有帮助。我需要在函数Wrapper(callback))中取出多余的右括号。
AggieMan

5

我建议使用一个包装器,该包装器将处理更改的值。例如,您可以具有JavaScript函数,如下所示:

function Variable(initVal, onChange)
{
    this.val = initVal;          //Value to be stored in this object
    this.onChange = onChange;    //OnChange handler

    //This method returns stored value
    this.GetValue = function()  
    {
        return this.val;
    }

    //This method changes the value and calls the given handler       
    this.SetValue = function(value)
    {
        this.val = value;
        this.onChange();
    }


}

然后,您可以从中创建一个对象,该对象将保存要监视的值,并且还可以创建一个在更改值时将调用的函数。例如,如果要在值更改且初始值为10时收到警告,则可以编写如下代码:

var myVar = new Variable(10, function(){alert("Value changed!");});

function(){alert("Value changed!");}调用时将调用处理程序(如果您查看代码)SetValue()

您可以像这样获得价值:

alert(myVar.GetValue());

您可以像这样设置值:

myVar.SetValue(12);

之后立即在屏幕上显示警报。看看它是如何工作的:http : //jsfiddle.net/cDJsB/


谢谢您的回答,但这并不是我想要的。请看我对aularon的评论!
Thanasis Petsas 2010年

2

该问题发布于很久以前,许多答案会定期汇总目标,如果目标不变,则会产生不必要的资源浪费。此外,大多数答案在等待原始帖子要求的更改时不会阻止程序。

现在,我们可以应用纯粹由事件驱动的解决方案。

该解决方案使用onClick事件来传递由值更改触发的事件。

该解决方案可以在支持Promise和async / await的现代浏览器上运行。如果您使用的是Node.js,请考虑将EventEmitter作为更好的解决方案。

<!-- This div is the trick.  -->
<div id="trick" onclick="onTrickClick()" />

<!-- Someone else change the value you monitored. In this case, the person will click this button. -->
<button onclick="changeValue()">Change value</button>

<script>

    // targetObj.x is the value you want to monitor.
    const targetObj = {
        _x: 0,
        get x() {
            return this._x;
        },
        set x(value) {
            this._x = value;
            // The following line tells your code targetObj.x has been changed.
            document.getElementById('trick').click();
        }
    };

    // Someone else click the button above and change targetObj.x.
    function changeValue() {
        targetObj.x = targetObj.x + 1;
    }

    // This is called by the trick div. We fill the details later.
    let onTrickClick = function () { };

    // Use Promise to help you "wait". This function is called in your code.
    function waitForChange() {
        return new Promise(resolve => {
            onTrickClick = function () {
                resolve();
            }
        });
    }

    // Your main code (must be in an async function).
    (async () => {
        while (true) { // The loop is not for pooling. It receives the change event passively.
            await waitForChange(); // Wait until targetObj.x has been changed.
            alert(targetObj.x); // Show the dialog only when targetObj.x is changed.
            await new Promise(resolve => setTimeout(resolve, 0)); // Making the dialog to show properly. You will not need this line in your code.
        }
    })();

</script>


1

您可以使用属性

Object.defineProperty MDN文档

例:

function def(varName, onChange) {
    var _value;

    Object.defineProperty(this, varName, {
        get: function() {
            return _value;
        },
        set: function(value) {
            if (onChange)
                onChange(_value, value);
            _value = value;
        }
    });

    return this[varName];
}

def('myVar', function (oldValue, newValue) {
    alert('Old value: ' + oldValue + '\nNew value: ' + newValue);
});

myVar = 1; // alert: Old value: undefined | New value: 1
myVar = 2; // alert: Old value: 1 | New value: 2

0

超级过时,但肯定是适应这一点的好方法。只是为一个项目写了这个,然后想了一下。与其他类似,风格各异。

var ObjectListener = function(prop, value) {

  if (value === undefined) value = null;

  var obj = {};    
  obj.internal = value;
  obj.watcher = (function(x) {});
  obj.emit = function(fn) {
    obj.watch = fn;
  };

  var setter = {};
  setter.enumerable = true;
  setter.configurable = true;
  setter.set = function(x) {
    obj.internal = x;
    obj.watcher(x);
  };

  var getter = {};
  getter.enumerable = true;
  getter.configurable = true;
  getter.get = function() {
    return obj.internal;
  };

  return (obj,
    Object.defineProperty(obj, prop, setter),
    Object.defineProperty(obj, prop, getter),
    obj.emit, obj);

};


user._licenseXYZ = ObjectListener(testProp);
user._licenseXYZ.emit(testLog);

function testLog() {
  return function() {
    return console.log([
        'user._licenseXYZ.testProp was updated to ', value
    ].join('');
  };
}


user._licenseXYZ.testProp = 123;

0

另外,您可以使一个函数根据其“静态”变量的值执行任务,例如:

在此处输入图片说明

<!DOCTYPE html>

<div id="Time_Box"> Time </div>

<button type="button" onclick='Update_Time("on")'>Update Time On</button>
<button type="button" onclick='Update_Time("off")'>Update Time Off</button>

<script>

var Update_Time = (function () {     //_____________________________________________________________

var Static = [];             //"var" declares "Static" variable as static object in this function

    return function (Option) {

    var Local = [];           //"var" declares "Local" variable as local object in this function

        if (typeof Option === 'string'){Static.Update = Option};

        if (Static.Update === "on"){
        document.getElementById("Time_Box").innerText = Date();

        setTimeout(function(){Update_Time()}, 1000);    //update every 1 seconds
        };

    };

})();  

Update_Time('on');    //turns on time update

</script>

0

对我有用的方法(我环顾四周,最终使用某人的jsfiddler /对其进行了很小的修改-很好地工作了)是使用getter和setter将该变量设置为对象,然后setter触发正在等待的函数可变的变化。

var myVariableImWaitingOn = function (methodNameToTriggerWhenChanged){
    triggerVar = this;
    triggerVar.val = '';
    triggerVar.onChange = methodNameToTriggerWhenChanged;
    this.SetValue(value){
        if (value != 'undefined' && value != ''){
            triggerVar.val = value; //modify this according to what you're passing in -
            //like a loop if an array that's only available for a short time, etc
            triggerVar.onChange(); //could also pass the val to the waiting function here
            //or the waiting function can just call myVariableImWaitingOn.GetValue()
        }
    };
    this.GetValue(){
        return triggerVar.val();
    };
 };

-1

JavaScript是有史以来最糟糕的程序\脚本语言之一!

用JavaScript似乎不可能“等待”!(是的,就像现实生活中一样,有时等待是最好的选择!)

我尝试了“ while”循环和“递归”(一个函数反复调用它直到...),但是JavaScript仍然拒绝工作!(这令人难以置信,但无论如何,请参见下面的代码:)

while循环:

<!DOCTYPE html>

<script>

var Continue = "no";
setTimeout(function(){Continue = "yes";}, 5000);    //after 5 seconds, "Continue" is changed to "yes"

while(Continue === 'no'){};    //"while" loop will stop when "Continue" is changed to "yes" 5 seconds later

    //the problem here is that "while" loop prevents the "setTimeout()" to change "Continue" to "yes" 5 seconds later
    //worse, the "while" loop will freeze the entire browser for a brief time until you click the "stop" script execution button

</script>

递归:

<!DOCTYPE html>

1234

<script>

function Wait_If(v,c){
if (window[v] === c){Wait_If(v,c)};
};

Continue_Code = "no"
setTimeout(function(){Continue_Code = "yes";}, 5000);    //after 5 seconds, "Continue_Code" is changed to "yes"

Wait_If('Continue_Code', 'no');

    //the problem here, the javascript console trows the "too much recursion" error, because "Wait_If()" function calls itself repeatedly!

document.write('<br>5678');     //this line will not be executed because of the "too much recursion" error above!

</script>

-2

不,您不必创建自己的解决方案。就像使用观察者设计模式之类的。

如果您无法控制变量或由谁使用它,恐怕您注定要失败。编辑:或使用Skilldrick的解决方案!

麦克风


1
我要说的是@JakeJ,您必须创建自己的解决方案。
Mike Gleason jr Couturier
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.