NEWS
[gelöst] Was ist der Scriptcontext?
-
Hi Ihr,
ich möchte eine function aus einem meiner JavaScript Scripte an eine externe Methode als Callback übergeben.
Dazu muss ich die function an den Context binden (myfunction.bind(myContext)).Leider hab ich keine Ahnung, welches der Context ist.
Kann mir da einer von Euch bitte helfen?
Danke!
-
Ein Scriptkontext oder auch Scope genannt, ist die aktuelle Sicht auf Variablen.
Der Kontext kann je nach Funktionsaufruf sich auch ändern.
Besonders bei Callback-Funktionen hat man hier immer wieder mal Schwierigkeiten
Beispiel
Du hast eine Funktion A innerhalb dessen Variablen benannt werden oder ein Objekt auf das du bspw mit this verweisen kannst.
Innerhalb dieser Funktion rufst du eine andere Funktion (B) auf, an die du eine Callback Funktion (C) übergibst. Diese Funktion macht irgendetwas und wenn das Ziel erreicht ist, wird deine Callback Funktion (C) aufgerufen. Blöderweise kennt diese Callback-Funktion (C) zum Zeitpunkt des Aufrufs die darüberliegenden Variablen oder this nicht, weil sie in einem anderen Kontext (nämlich dieser Benutzerfunktion B) aufgerufen wurde. Um das zu beheben, musste man vor ES6 die bind-Funktion verwenden.Seit ES6 kann man dafür ein anderes Konstrukt verwenden, was es auch lesbarer macht
Wenn du ein Link für die Doku hast, dann könnte man dir direkt helfen
Beispiel
///Funktioniert nicht Function A() { var xxx="xxx"; var yyy="yyy"; B(yyy,function(zzz) { //mache irgendwas wenn Funktion B fertig ist //xxx ist hier nicht bekannt, da sie in einem anderen Scope definiert worden ist } } ///Funktioniert Function A() { var xxx="xxx"; var yyy="yyy"; B(yyy,function(xxx,zzz) { //mache irgendwas wenn Funktion B fertig ist //xxx ist hier bekannt, da sie an die Funktion gebunden wurde und als erster Parameter an den Callback übergeben wird. }.bind(xxx); } ///Neue Variante ab ES6 (iobroker node kann bereits seit langem es6 //bitte achte auf die deklaration mit let und die andere Definitionsweise der Callbackfunktion, das nennt sich anonymous arrow function Function A() { let xxx="xxx"; let yyy="yyy"; B(yyy,(zzz) =>{ //mache irgendwas wenn Funktion B fertig ist //xxx ist hier bekannt, da sie an die Funktion gebunden wurde und als erster Parameter an den Callback übergeben wird. }; }
Link bind
Link Arrow Function
Link Let -
@oliverio Danke, das ist sicher für die meisten eine nette Erklärung, aber welchen Context muss ich an eine im Script befindliche Funktion binden, damit die ihren Context behält?
-
@great-sun
so wie ich geschrieben habe.
entweder ein objekt oder variable, die du ausserhalb definiert hast
oder bspw this.
je nachdem was du benötigst um beim aufruf der callback-funktion weiterarbeiten zu können.
das kann halt nicht pauschal beantwortet werden, da das von der funktion an sich abhängt.
wenn du einen debugger verwenden würdest, dann würdest du sehen, in welcher Zeile, welche Scope/Kontexte verfügbar sind.Hast du den kein Code-Beispiel oder Doku, aus der du diese Informationen hast?
So aus 2.Hand lässt sich das nicht sagen -
function getObjectValue(objectPath) { return getState(objectPath).val; } const autoConfigClass = new AutoConfigClass(getObjectValue.bind(this));
Da weder this noch global oder globalThis hier zu funktionieren scheinen, kommt halt immer wieder:
TypeError: this.getObjects is not a function
-
-
@oliverio Das ist was selbstgeschriebenes.
-
@great-sun sagte in Was ist der Scriptcontext?:
this.getObjects
dies erscheint leider in deinem Beispielcode nirgends
-
@great-sun sagte in Was ist der Scriptcontext?:
Das ist was selbstgeschriebenes
von dir? dann müsstest doch wissen was da übergeben wird.
Wenn von jemand anderem, dann den code hier posten oder denjenigen Fragen -
Ich hab ein Script das ganz normal unter Scripte im ioBroker läuft. In dem ist die Funktion
function getObjectValue(objectPath) { return getState(objectPath).val; }
Und von dort aus initialisiere ich die Klasse, der ich die lokale Funktion als Callback übergeben will,
Das funktionier ausserhalb mit zwei Klassen Problemlos, aber in ioBroker finde ich scheinbar einfach nicht den richtigen binding context.in der Klasse ist das einfach so:
constructor(getObjects) { this.getObjects = getObjects; }
Später wird dann halt irgendwo die Methode this.getObjects mit dem entsprechenden Pfad aufgerufen.
Wenn ich bei der Instanziierung also den richtigen Binding-Context übergebe, sollte das rein in meiner Theorie funktionieren.Siehst Du das irgendwie anders?
Ein Beispiel mit zwei Klassen, das funktioniert:
PlayCode Beispiel -
und woher hast du, das du da einen Kontext übergeben musst?
Aktuell musst du da gar nichts übergeben, da getState ja auch innerhalb des Objekts AutoConfigClass funktioniert, da es generell vom iobroker als Befehl bereitgestellt wird.
Alternativ übergebe nur den ObjectPath an das ObjektAuch wenn ich dir das nicht rate, weil das viel zu kompliziert und nicht mehr lesbar wäre, kann man einen funktionsaufruf inklusive der Parameter einfrieren und zur späteren Ausführung aufheben.
Das wäre dann
apply
Aber das habe ich noch nie verwendet.übertragen auf dein erstes Beispiel müsste es dann so aussehen
const autoConfigClass = new AutoConfigClass(getObjectValue.apply(null,"javascript.0.test1"));
Der Inhalt des Parameters muss dann aber bekannt sein, sonst macht das keinen Sinn.
-
Na toll....
Das Problem war... Er hat mir beim Aufruf einer globalen Funktion noch eine andere Instanziierung gemacht, an die ich gar nicht mehr gedacht hatte. Im Log stand dann halt nur das Script, das ich ausgeführt hab, weswegen ich immer wieder dachte, das hängt an dem Script....Danke trotzdem fürs Engagement und die beständige Hilfe/Suche nach dem Fehler.
Pebcak
-
noch ne anmerkung zu deinem playground
alle required anweisungen an den anfang stellen.
insbesondere willst du das nicht immer wieder neu ausführen (handeData)sich innerhalb einer instanziierung eines objektes nicht veränderlichen daten, sollten auch nicht immer wieder neu berechnet werden handleData,Berechnung filePath
Wiederholter Dateizugriff auf die gleiche Ressource innerhalb kürzester Zeit gilt als inperformant. Daher sollten die Daten im Speicher gesammelt werden und dann nur einmal geschrieben werden. In der Schleife wird saveHash innerhalb weniger Millisekunden mehfach aufgerufen.
Die Übergabe des Callbacks müsste eigentlich auch ganz simple funktionieren
const testClass = new TestClass(dataClass.handleData);
in TestClass wird der Functionpointer dann gespeichert und später dann in setData zusammen mit den Parametern aufgerufen. -
Generell bin ich voll bei Dir, das war nur zur Vereinfachung, dass jeder weiß, was gemeint/genutzt wird im playground.
Die Übergabe des Callbacks müsste eigentlich auch ganz simple funktionieren
const testClass = new TestClass(dataClass.handleData);
in TestClass wird der Functionpointer dann gespeichert und später dann in setData zusammen mit den Parametern aufgerufen.Das geht nur mit .bind(dataClass) sonst heißt es is not a function