Как я могу заставить программу ждать изменения переменной в JavaScript? - PullRequest
57 голосов
/ 03 сентября 2010

Я хочу заставить программу JavaScript ждать в определенных точках своего выполнения, пока не изменится переменная. Есть ли способ сделать это? Я уже нашел расширение, которое называется «нарративный JavaScript» и заставляет программу ждать, пока событие не произойдет. Есть ли способ создать новое событие, например, «событие изменения переменной», которое ведет себя как событие onclick ..

Ответы [ 9 ]

84 голосов
/ 03 сентября 2010

Изменить 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();
20 голосов
/ 03 сентября 2010

Интерпретаторы JavaScript являются однопоточными, поэтому переменная никогда не может измениться, когда код ожидает другого кода, который не меняет переменную.

По моему мнению, было бы лучшим решением обернуть переменную в некоторый объект, который имеет функции получения и установки. Затем вы можете зарегистрировать функцию обратного вызова в объекте, который вызывается при вызове функции-установщика объекта. Затем вы можете использовать функцию получения в обратном вызове для получения текущего значения:

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>
5 голосов
/ 03 сентября 2010

Я бы порекомендовал оболочку, которая будет обрабатывать изменяемые значения.Например, у вас может быть функция 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/

1 голос
/ 14 марта 2019

Вопрос был опубликован давным-давно, многие ответы периодически объединяют цели и приводят к ненужной растрате ресурсов, если цель не изменяется.Кроме того, большинство ответов не блокируют программу во время ожидания изменений, как того требует исходное сообщение.

Теперь мы можем применить решение, которое основано исключительно на событиях.

Решение использует 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 голос
/ 29 ноября 2014

Вы можете использовать свойства :

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 голосов
/ 16 июня 2019

В качестве альтернативы, вы можете создать функцию, которая выполняет задачи, основываясь на значении ее «статических» переменных, например ниже:

enter image description here

<!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 голосов
/ 16 июня 2019

JavaScript - один из худших программных / скриптовых языков в истории!

«Ждать» в JavaScript невозможно! (Да, как и в реальной жизни, иногда ожидание - лучший вариант!)

Я пробовал цикл while и Recursion (функция вызывает себя несколько раз, пока ...), но 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>
0 голосов
/ 07 июня 2019

Супер устаревший, но, безусловно, хорошие способы приспособить это. Просто написал это для проекта и решил, что я поделюсь. Как и некоторые другие, разнообразны по стилю.

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 голосов
/ 03 сентября 2010

Нет, вам бы пришлось создать собственное решение. Например, использовать шаблон проектирования Observer или что-то в этом роде.

Если вы не можете контролировать переменную или кто ее использует, боюсь, вы обречены. РЕДАКТИРОВАТЬ: Или использовать решение Skilldrick!

Mike

...