我想强制 JavaScript 程序在其执行的某些特定点等待,直到变量发生更改。有没有办法做到这一点?我已经找到了一个名为“narrative JavaScript”的扩展,它强制程序等待事件发生。有没有办法创建一个新事件,例如一个“变量更改事件”,其行为类似于 onclick 事件..
如何让程序等待 javascript 中的变量更改?
2018 年编辑:请查看Object getter 和 setter以及Proxies。下面的旧答案:
一个快速简单的解决方案是这样的:
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();
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>
我会推荐一个包装器来处理被改变的值。例如,您可以拥有 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!");});
Handlerfunction(){alert("Value changed!");}
将在被调用时被调用(如果您查看代码)SetValue()
。
你可以获得这样的value:
alert(myVar.GetValue());
您可以像这样设置值:
myVar.SetValue(12);
紧接着,屏幕上将显示警报。看看它是如何工作的:http : //jsfiddle.net/cDJsB/
对我有用的(我到处查看并最终使用了某人的 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();
};
};
这个问题很早以前就贴出来了,很多答案会周期性地汇集目标,如果目标不变,就会产生不必要的资源浪费。此外,大多数答案在等待原始帖子要求的更改时不会阻止程序。
我们现在可以应用纯事件驱动的解决方案。
该解决方案使用 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>