【问题标题】:How can I make a program wait for a variable change in javascript?如何让程序等待 javascript 中的变量更改?
【发布时间】:2011-04-07 20:20:20
【问题描述】:

我想强制 JavaScript 程序在其执行的某些特定点等待,直到变量发生变化。有没有办法做到这一点?我已经找到了一个名为“narrative JavaScript”的扩展,它强制程序等待事件发生。有没有办法创建一个新事件,例如“变量更改事件”,其行为类似于 onclick 事件..

【问题讨论】:

  • 我认为最好的方法是使用 rxjs 库并使用 observable 作为变量。

标签: javascript variables wait


【解决方案1】:

基于更多功能的 Promise (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);
})();

【讨论】:

    【解决方案2】:

    对我有用的东西(我到处查看并最终使用某人的 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();
        };
     };
    

    【讨论】:

      【解决方案3】:

      或者,您可以创建一个函数,根据其“静态”变量的值执行任务,示例如下:

      <!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>
      

      【讨论】:

        【解决方案4】:

        超级过时,但肯定是适应这种情况的好方法。刚写了这个 对于一个项目,我想我会分享。与其他一些类似,风格各异。

        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;
        

        【讨论】:

          【解决方案5】:

          问题是很久以前的问题,很多答案会周期性地集中目标,如果目标不变,会产生不必要的资源浪费。此外,大多数答案在等待原帖要求的更改时不会阻塞程序。

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

          该方案使用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】:

            编辑 2018:请查看 Object getters and settersProxies。下面的旧答案:


            一个快速简单的解决方案如下:

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

            【讨论】:

            • 现在已编辑,它每 50 毫秒检查一次,适用于非布尔值。正如我所说的那样快速简便的方法,请点击此链接:stackoverflow.com/questions/1029241/…(@mplungjan 已发布)以获得更深入的问题证明解决方案。
            • 我想你的意思是something===something_cachedValue。然后它工作:jsfiddle.net/pEnYC
            • @Skilldrick,是的,我的意思是,我修好了。谢谢指正:)
            • 谢谢大家的回答!但在所有解决方案中,程序都不会阻塞!它将继续正常执行,如果变量在执行过程中发生变化,则会调用回调。但是,我想做的是强制程序阻止它的执行,直到变量改变(通过调用一个函数),然后从它被阻塞的地方继续!有什么想法吗?
            • 像这家伙一样,让变量触发你想要的方法。这样该方法就不会等待任何东西,它会在变量准备好时被调用。 jsfiddle.net/cDJsB
            【解决方案7】:

            您可以使用属性

            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
            

            【讨论】:

              【解决方案8】:

              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>
              

              【讨论】:

              • 如果您可以控制原始变量,这可能是最好的解决方案。
              • 感谢您的回答,但这并不是我想要的。请看我对 aularon 的评论!
              • 正如我所解释的,JavaScript 解释器是单线程的。您不能阻塞线程并使其等待其他线程更改变量,因为没有其他线程可以更改变量。当另一个已经在执行时,不能执行任何 JavaScript 事件处理程序。
              • 它按描述工作。谢谢!!!这很有帮助。我需要在函数 Wrapper(callback)) 中取出多余的右括号。
              【解决方案9】:

              我会推荐一个包装器来处理被改变的值。例如你可以有 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!");});
              

              SetValue() 被调用时,处理程序function(){alert("Value changed!");} 将被调用(如果你看代码的话)。

              你可以像这样获得价值:

              alert(myVar.GetValue());
              

              你可以像这样设置值:

              myVar.SetValue(12);
              

              紧接着,屏幕上会显示一条警报。看看它是如何工作的:http://jsfiddle.net/cDJsB/

              【讨论】:

              • 感谢您的回答,但这并不是我想要的。请看我对 aularon 的评论!
              【解决方案10】:

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

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

              迈克

              【讨论】:

              • @JakeJ 这就是我的意思,您必须创建自己的解决方案。
              猜你喜欢
              • 1970-01-01
              • 2011-09-13
              • 1970-01-01
              • 1970-01-01
              • 2022-07-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多