NEWS
[Frage] Wie ist der richtiger Umgang mit Callbacks?
-
Hallo zusammen,
ich arbeite nach wie vor an meiner ioBroker.homehub Visualisierung. Ich kann die Konfiguration der Instanz einlesen und States anzeigen.
Jetzt möchte ich noch die Objekte einlesen. Das Ganze soll in folgender Reihenfolge passieren: Erst die Konfiguration der Instanz, dann die Objekte und dann die States. Irgendwie kriege ich das mit den Callbacks aber nicht auf die Reihe.
Hier mal der Code, den ich momentan habe:
function initializeHomehub(){ console.log('Initializing HomeHub'); // Reset everything config['initiated'] = false; config['categories'] = []; config['objects'] = []; config['states'] = []; // Use callbacks to first fetch configuration then objects and then states fetchStates( fetchObjects( fetchConfiguration() ) ); } function fetchConfiguration(callback) { callback = (typeof callback === 'function') ? callback : function() {}; console.log('Fetching configuration'); servConn.getObject('system.adapter.homehub.0', false, function (error, obj) { console.log('Received configuration.'); config['categories'] = obj['native']['categories']; callback; }); } function fetchObjects(callback) { callback = (typeof callback === 'function') ? callback : function() {}; console.log('Fetching objects'); servConn.getObjects(function (err, _objects) { console.log('Received objects.'); config['objects'] = _objects; callback; }); } function fetchStates(callback) { callback = (typeof callback === 'function') ? callback : function() {}; console.log('Fetching states'); servConn.getStates(function (err, _states) { console.log('Received states.'); config['states'] = _states; config.initiated = true; callback; }); }
Die Funktion initializeHomehub() soll einmal aufgerufen werden und dann die anderen Funktionen aufrufen. Die Ergebnisse sollen jeweils in die Variable config geschrieben werden.
Die Ergebnisse werden in die config Variable geschrieben, aber in der Reihenfolge Konfiguration, States, Objects.
Hat jemand einen Tipp für mich?
Gruß,
Markus -
wenn du den callback aufrufst, müsste das dann nicht
callback();
heißen, anstatt einfach nur
callback;
-
Zuerst das was @OliverW sagt, wenn der callback ausgeführt werden soll, musst du das auch mit
callback(); //Klammern!
machen.
Dann darfst du im Teil
fetchStates( fetchObjects( fetchConfiguration() ) );
Nicht die "aufgerufenen Funktionen" d.h. ihre Ergebnisse übergeben, sondern lediglich die Referenz auf die Funktion:
// rufe die fetchObjects Funktion auf und übergebe ihr die Funktion fetchConfiguration als Callback fetchObjects( fetchConfiguration )
Deine Dreier-Kette würde dann so aussehen:
fetchStates(fetchObjects.bind(null, fetchConfiguration));
Hier wird zuerst eine "virtuelle Methode" erstellt, die das gleiche tut wie fetchObjects, mit dem scope=null und dem vorausgefüllten Parameter callback = fetchConfiguration (das ist fachlich falsch, aber als Erklärung in Ordnung, denke ich).
Diese "virtuelle Methode" wird als Callback der fetchStates Funktion übergeben.
Jetzt wird zuerst fetchStates ausgeführt, (wenn du den oben genannten Fix gemacht hast) die auf fetchObjects "virtuelle Methode" und dann im Anschluss fetchConfiguration.Nichtsdestotrotz rate ich dir aber, entweder mit Promises oder direkt async / await zu arbeiten, die machen dir das Leben viel leichter.
Änderst du deine Methoden wie folgt ab:function fetchConfiguration(callback) { console.log('Fetching configuration'); return new Promise((resolve, reject) => { servConn.getObject('system.adapter.homehub.0', false, function (error, obj) { if (error) { return reject(error); } console.log('Received configuration.'); config['categories'] = obj['native']['categories']; resolve(); }); }); } function fetchObjects(callback) { return new Promise((resolve, reject) => { console.log('Fetching objects'); servConn.getObjects(function (err, _objects) { if (err) { return reject(err); } console.log('Received objects.'); config['objects'] = _objects; resolve(); }); }); } function fetchStates(callback) { return new Promise((resolve, reject) => { console.log('Fetching states'); servConn.getStates(function (err, _states) { if (err) { return reject(err); } console.log('Received states.'); config['states'] = _states; config.initiated = true; resolve(); }); }); }
kannst du sie so zusammen setzen:
fetchStates() .then(fetchObjects) .then(fetchConfiguration) .then(() => { console.log("Done") }) //or something useful .catch(console.error) //or something useful
bzw.
//für await ist noch etwas vorarbeit erforderlich, aber die spare ich hier und heute aus await fetchStates(); await fetchObjects(); await fetchConfiguration();
was alles viel viel lesbarer ist (und dir die Callback Hell erspart)
-
@tim3trick sagte in [Frage] Wie ist der richtiger Umgang mit Callbacks?:
fetchStates(fetchObjects.bind(null, fetchConfiguration));
Hier wird zuerst eine "virtuelle Methode" erstellt, die das gleiche tut wie fetchObjects, mit dem scope=null und dem vorausgefüllten Parameter callback = fetchConfiguration (das ist fachlich falsch, aber als Erklärung in Ordnung, denke ich).
Das funktioniert zwar, lesbarer und verständlicher ist IMO aber folgende Variante:
fetchStates( () => fetchObjects( () => fetchConfiguration() ) );
Die ist außerdem näher dran an dem, was @braindead zuerst hatte (nur dass er die Funktionen direkt aufgerufen hat, statt Callbacks zu definieren.
-
Vielen Dank für Eure Hilfe. Kaum mache ich es richtig, funktioniert es genauso, wie es sein soll.