我想强制JavaScript程序在其执行的某些特定点等待,直到变量发生变化。有办法吗?我已经找到了一个名为“叙事JavaScript”的扩展,它强制程序等到事件发生。有没有办法创建一个新事件,例如“变量事件”,就像onclick事件一样..
答案 0 :(得分:85)
修改2018:请查看Object getters and setters和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();
答案 1 :(得分:19)
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 :(得分: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/
答案 3 :(得分:1)
您可以使用属性:
Object.defineProperty MDN documentation
示例:
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
答案 4 :(得分:0)
不,你必须创建自己的解决方案。就像使用Observer设计模式一样。
如果您无法控制变量或使用变量,我恐怕您注定失败了。 编辑:或者使用Skilldrick的解决方案!
麦克
答案 5 :(得分:0)
问题是很久以前发布的,许多答案会定期汇总目标,如果目标不变,则会产生不必要的资源浪费。此外,大多数答案在等待原始帖子要求的更改时不会阻止程序。
我们现在可以应用一种纯粹由事件驱动的解决方案。
该解决方案使用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>
答案 6 :(得分: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;
答案 7 :(得分:0)
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>
答案 8 :(得分: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>
答案 9 :(得分: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();
};
};
答案 10 :(得分:0)
基于功能性承诺(async/await)的方法示例:
var delay = require('delay');
var obj = {
k: null
};
function notAwaitable() {
return obj.k;
}
async function waitFor(f) {
let r = f();
while (!r) {
console.log('Not yet, waiting more');
await delay(1000);
r = f();
}
return r;
}
(async function() {
await delay(5000);
obj.k = 42;
})();
(async function() {
let result = await waitFor(notAwaitable);
console.log(result);
})();