web-dev-qa-db-fra.com

Comment puis-je faire attendre un programme pour un changement de variable en javascript?

Je veux forcer un programme JavaScript à attendre certains points particuliers de son exécution jusqu'à ce qu'une variable ait changé. Y a-t-il un moyen de le faire? J'ai déjà trouvé une extension appelée "JavaScript narratif" qui oblige le programme à attendre qu'un événement se produise. Existe-t-il un moyen de créer un nouvel événement, un "événement de changement de variable" par exemple qui se comporte comme un événement onclick.

57
Thanasis Petsas

Edit 2018: Veuillez regarder Obturateurs et setters d'objets et Proxies. Ancienne réponse ci-dessous:


une solution rapide et facile va comme ceci:

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

Les interpréteurs JavaScript sont à thread unique, donc une variable ne peut jamais changer, lorsque le code attend dans un autre code qui ne change pas la variable.

À mon avis, ce serait la meilleure solution pour envelopper la variable dans une sorte d'objet qui a une fonction getter et setter. Vous pouvez ensuite enregistrer une fonction de rappel dans l'objet qui est appelé lorsque la fonction de définition de l'objet est appelée. Vous pouvez ensuite utiliser la fonction getter dans le rappel pour récupérer la valeur actuelle:

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

Cela pourrait être facilement utilisé comme ceci:

<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>
20
Reboot

Je recommanderais un wrapper qui gérera la valeur en cours de modification. Par exemple, vous pouvez avoir une fonction JavaScript, comme ceci:

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


}

Et puis vous pouvez en faire un objet qui contiendra la valeur que vous souhaitez surveiller, ainsi qu'une fonction qui sera appelée lorsque la valeur sera modifiée. Par exemple, si vous souhaitez être alerté lorsque la valeur change et que la valeur initiale est 10, vous écrirez du code comme ceci:

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

Le gestionnaire function(){alert("Value changed!");} sera appelé (si vous regardez le code) lorsque SetValue() est appelé.

Vous pouvez obtenir de la valeur comme ceci:

alert(myVar.GetValue());

Vous pouvez définir une valeur comme ceci:

myVar.SetValue(12);

Et immédiatement après, une alerte sera affichée à l'écran. Voir comment cela fonctionne: http://jsfiddle.net/cDJsB/

5
Cipi

Vous pouvez utiliser les propriétés :

documentation MDN Object.defineProperty

Exemple:

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
1
Eduardo Cuomo

La question a été publiée il y a longtemps, de nombreuses réponses regroupent périodiquement l'objectif et produisent un gaspillage inutile de ressources si l'objectif reste inchangé. En outre, la plupart des réponses ne bloquent pas le programme en attendant les modifications requises par le message d'origine.

Nous pouvons désormais appliquer une solution purement événementielle.

La solution utilise l'événement onClick pour fournir un événement déclenché par un changement de valeur.

La solution peut être exécutée sur des navigateurs modernes qui prennent en charge Promise et async/wait. Si vous utilisez Node.js, considérez EventEmitter comme une meilleure solution.

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

Ce qui a fonctionné pour moi (j'ai regardé partout et j'ai fini par utiliser le jsfiddler de quelqu'un/le modifiant très légèrement - a bien fonctionné) était de définir cette variable sur un objet avec un getter et un setter, et le setter déclenche la fonction qui attend changement de variable.

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();
    };
 };
0
JakeJ

Super daté, mais certainement de bonnes façons d'accommoder cela. Je viens d'écrire ceci pour un projet et j'ai pensé que je partagerais. Semblable à certains des autres, de style varié.

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
darcher

JavaScript est l'un des pires langages de programmation et de programmation de tous les temps!

"Attendre" semble impossible en JavaScript! (Oui, comme dans la vraie vie, parfois attendre est la meilleure option!)

J'ai essayé la boucle "while" et la "récursion" (une fonction s'appelle à plusieurs reprises jusqu'à ...), mais JavaScript refuse de fonctionner quand même! (C'est incroyable, mais de toute façon, voyez les codes ci-dessous :)

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

Récursivité:

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

Alternativement, vous pouvez créer une fonction qui exécute des tâches en fonction de la valeur de ses variables "statiques", exemple ci-dessous:

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
User

Non, vous devrez créer votre propre solution. Comme utiliser le modèle de conception Observer ou quelque chose.

Si vous n'avez aucun contrôle sur la variable ou qui l'utilise, je crains que vous ne soyez condamné. EDIT: Ou utilisez la solution de Skilldrick!

Mike