Wie kann ich ein Programm auf eine variable Änderung in Javascript warten lassen?

69

Ich möchte ein JavaScript-Programm zwingen, an bestimmten Punkten seiner Ausführung zu warten, bis sich eine Variable geändert hat. Gibt es eine Möglichkeit, dies zu tun? Ich habe bereits eine Erweiterung namens "narratives JavaScript" gefunden, die das Programm zwingt, auf das Eintreten eines Ereignisses zu warten. Gibt es eine Möglichkeit, ein neues Ereignis zu erstellen, beispielsweise ein "Variablenänderungsereignis", das sich wie ein Ereignis mit einem Klick verhält?

Thanasis Petsas
quelle
Ich denke, der beste Weg ist, rxjs Bibliothek zu verwenden und eine beobachtbare als Ihre Variable zu verwenden.
Az264

Antworten:

91

Edit 2018: Bitte schauen Sie sich Object Getter und Setter und Proxies an . Alte Antwort unten:


Eine schnelle und einfache Lösung lautet wie folgt:

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();
Aularon
quelle
1
Jetzt bearbeitet, überprüft es alle 50 ms und funktioniert für Nicht-Bools. Wie gesagt, der schnelle und einfache Weg, dies zu tun, folgen Sie diesem Link: stackoverflow.com/questions/1029241/… (bereits von @mplungjan gepostet) für eine detailliertere problemsichere Lösung.
Aularon
Ich denke du meintest something===something_cachedValue. Dann funktioniert es: jsfiddle.net/pEnYC
Skilldrick
@ Skilldrick, ja das habe ich gemeint, ich habe es behoben. Danke für die Korrektur
:)
1
Vielen Dank für Ihre Antworten! Aber in allen Lösungen wird das Programm nicht blockieren! Die Ausführung wird normal fortgesetzt. Wenn sich die Variable während der Ausführung ändert, wird ein Rückruf aufgerufen. Was ich aber tun möchte, ist, das Programm zu zwingen, seine Ausführung bis zur Änderung der Variablen zu blockieren (durch Aufrufen einer Funktion), und dann an diesem Punkt fortzufahren, an dem es blockiert wurde! Irgendwelche Ideen?
Thanasis Petsas
Lassen Sie die Variable wie dieser Typ die gewünschte Methode auslösen. Auf diese Weise wartet die Methode nicht auf etwas, sondern wird aufgerufen, wenn die Variable bereit ist. jsfiddle.net/cDJsB
JakeJ
24

JavaScript-Interpreter sind Single-Threaded-Interpreter, sodass sich eine Variable niemals ändern kann, wenn der Code in einem anderen Code wartet, der die Variable nicht ändert.

Meiner Meinung nach wäre es die beste Lösung, die Variable in eine Art Objekt zu verpacken, das eine Getter- und Setter-Funktion hat. Sie können dann eine Rückruffunktion in dem Objekt registrieren, das aufgerufen wird, wenn die Setterfunktion des Objekts aufgerufen wird. Sie können dann die Getter-Funktion im Rückruf verwenden, um den aktuellen Wert abzurufen:

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

Dies könnte leicht wie folgt verwendet werden:

<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>
Starten Sie neu
quelle
2
Wahrscheinlich die beste Lösung, wenn Sie die Kontrolle über die ursprüngliche Variable haben.
Skilldrick
Vielen Dank für die Antwort, aber es ist nicht genau das, was ich will. Schauen Sie bitte meinen Kommentar zu aularon!
Thanasis Petsas
1
Wie ich erklärt habe, sind JavaScript-Interpreter Single-Threaded. Sie können den Thread nicht blockieren und warten lassen, bis ein anderer Thread eine Variable ändert, da es keinen anderen Thread gibt, der eine Variable ändern kann. Es kann kein JavaScript-Ereignishandler ausgeführt werden, während bereits ein anderer ausgeführt wird.
Starten Sie den
Es funktioniert wie beschrieben. Vielen Dank!!! Das war sehr hilfreich. Ich musste die extra rechte Klammer in der Funktion Wrapper (Rückruf) entfernen.
AggieMan
5

Ich würde einen Wrapper empfehlen, der den geänderten Wert verarbeitet. Zum Beispiel können Sie eine JavaScript-Funktion haben, wie folgt:

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


}

Und dann können Sie daraus ein Objekt erstellen, das den Wert enthält, den Sie überwachen möchten, sowie eine Funktion, die aufgerufen wird, wenn der Wert geändert wird. Wenn Sie beispielsweise benachrichtigt werden möchten, wenn sich der Wert ändert und der Anfangswert 10 ist, schreiben Sie Code wie folgt:

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

Der Handler function(){alert("Value changed!");}wird aufgerufen (wenn Sie sich den Code ansehen), wenn er SetValue()aufgerufen wird.

Sie können Wert wie folgt erhalten:

alert(myVar.GetValue());

Sie können den Wert folgendermaßen einstellen:

myVar.SetValue(12);

Unmittelbar danach wird eine Warnung auf dem Bildschirm angezeigt. Sehen Sie, wie es funktioniert: http://jsfiddle.net/cDJsB/

Cipi
quelle
Vielen Dank für die Antwort, aber es ist nicht genau das, was ich will. Schauen Sie bitte meinen Kommentar zu aularon!
Thanasis Petsas
2

Die Frage wurde vor langer Zeit gestellt, viele Antworten bündeln das Ziel regelmäßig und verursachen unnötige Ressourcenverschwendung, wenn das Ziel unverändert bleibt. Darüber hinaus blockieren die meisten Antworten das Programm nicht, während sie auf Änderungen warten, die im ursprünglichen Beitrag gefordert werden.

Wir können jetzt eine Lösung anwenden, die rein ereignisgesteuert ist.

Die Lösung verwendet das Ereignis onClick, um ein Ereignis bereitzustellen, das durch eine Wertänderung ausgelöst wird.

Die Lösung kann in modernen Browsern ausgeführt werden, die Promise und async / await unterstützen. Wenn Sie Node.js verwenden, sollten Sie EventEmitter als bessere Lösung in Betracht ziehen .

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

Homer Liu
quelle
1

Sie können Eigenschaften verwenden :

Object.defineProperty MDN-Dokumentation

Beispiel:

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
Eduardo Cuomo
quelle
0

Super veraltet, aber sicherlich gute Möglichkeiten, dies unterzubringen. Ich habe das gerade für ein Projekt geschrieben und dachte, ich würde es teilen. Ähnlich wie bei einigen anderen, unterschiedlich im Stil.

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;
Darcher
quelle
0

Alternativ können Sie eine Funktion erstellen, die Aufgaben basierend auf dem Wert ihrer "statischen" Variablen ausführt. Beispiel unten:

Geben Sie hier die Bildbeschreibung ein

<!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>
Nutzer
quelle
0

Was für mich funktioniert hat (ich habe überall nachgesehen und schließlich den jsfiddler von jemandem verwendet / es leicht modifiziert - hat gut funktioniert), war, diese Variable auf ein Objekt mit einem Getter und einem Setter zu setzen, und der Setter löst die Funktion aus, auf die er wartet Variablenänderung.

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

JavaScript ist eine der schlechtesten Programm- / Skriptsprachen aller Zeiten!

"Warten" scheint in JavaScript unmöglich zu sein! (Ja, wie im wirklichen Leben ist manchmal Warten die beste Option!)

Ich habe "while" -Schleife und "Rekursion" versucht (eine Funktion ruft sich wiederholt auf, bis ...), aber JavaScript funktioniert trotzdem nicht! (Das ist unglaublich, aber trotzdem, siehe die Codes unten :)

while-Schleife:

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

Rekursion:

<!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>
Nutzer
quelle
-2

Nein, Sie müssten Ihre eigene Lösung erstellen. Wie mit dem Observer-Designmuster oder so.

Wenn Sie keine Kontrolle über die Variable haben oder wer sie verwendet, sind Sie leider zum Scheitern verurteilt. EDIT: Oder verwenden Sie Skilldricks Lösung!

Mike

Mike Gleason jr. Couturier
quelle
1
@JakeJ das sage ich, du müsstest deine eigene Lösung erstellen.
Mike Gleason jr. Couturier