NEWS
Asynchron bzw mir fehlt der Lösungsansatz
-
ich habe folgendes Progrämmchen, welches mir die maximales Ventilöffnung von Heizkörperventilen eines Gewerkes ermitteln soll
$('[state.id=*.VALVE_STATE](functions=Ventil_Anbau)').on (function (obj) { var MaxValve=-1; $('[state.id=*.VALVE_STATE](functions=Ventil_Anbau)').each (function (id, i) { getState (id, function (error,oID) { if (oID.val>MaxValve) { MaxValve= oID.val; } log (oID.val + ", "+ MaxValve); }); }); log ("Maximal: " + MaxValve); });
sieht logisch aus, tuts aber leider nicht, da die $.each asychron läuft
Der Aufruf tuts, aber wenn die Routine durchläuft, ergibt sich folgende Ausgabe
Maximal: -1
16, 16
5, 16,
32, 32
8, 32
Fragestellung:
Wie kriege ichs hin, das Ausgabe von Maximal (später dann ein setState () ) das Schleifenergebnis ausgibt ?
Danke für hilfreiche Ideen, Gruss und guten Rutsch, Black
-
Disclaimer: Alles ungetestet!
Wie kriege ichs hin, das Ausgabe von Maximal (später dann ein setState () ) das Schleifenergebnis ausgibt ? `
Die $-Funktion gibt ein Array zurück, das um ein paar Funktionen (on/each/…) erweitert wurde. Du kannst also vorher die Länge abfragen und dann beim letzten Eintrag die finale Logik ausführen:! ````
var valveIDs = $('state.id=*.VALVE_STATE');
! valveIDs.on(function (obj) {
var MaxValve=-1;
valveIDs.each(function (id, i) {
getState(id, function (error,oID) {
if (oID.val>MaxValve) {
MaxValve = oID.val;
}
log(oID.val + ", "+ MaxValve);if (i === valveIDs.length - 1) { log ("Maximal: " + MaxValve); // Weiterer Code, der zum Abschluss der Prüfung ausgeführt wird } }); });
});
! ````
Ich würde aber behaupten, dass nicht die .each-Funktion asynchron ausgeführt wird, sondern dass dein Problem daher kommt, dass du die asynchrone Variante von getState (mit Callback) verwendest. Einfacher geht es so:! ````
var valveIDs = $('state.id=*.VALVE_STATE');
! valveIDs.on(function (obj) {
var MaxValve=-1;
valveIDs.each(function (id, i) {
// Hier liegt der Hase im Pfeffer:
var curVal = getState(id);if (curVal > MaxValve) { MaxValve = curVal; } log(curVal + ", "+ MaxValve); }); log ("Maximal: " + MaxValve); // Weiterer Code, der zum Abschluss der Prüfung ausgeführt wird
});
-
ok, dann werde ich das mal angehen.
ich habe alle getstates bei mir aber asynchron, ich habe 3 javascript adapter bei mir laufen, 1* produktiv, 1* device wrapper und 1* spiele und test adapter. der Unterschied bei load und CPU Temp mit haken bei "nicht alle Zustände bei Start abonieren" ist deutllich.
von daher fände ich es nett, wenn es eine trickreichemöglichkeit gäbe, einen asychronen block irgendwie zu kapseln.
ich werd deinen Ansatz mal testen, thnx erstmal, Black
-
ich habe alle getstates bei mir aber asynchron, `
Dann solltest Du auch wissen, wie man damit umgeht. Für alle anderen gibt es die synchrone Version. -
if (i === valveIDs.length - 1) { log ("Maximal: " + MaxValve); // Weiterer Code, der zum Abschluss der Prüfung ausgeführt wird } ```` `
Das wird wahrscheinlich auch nicht funktionieren, da i schneller durchgezählt wird als die callbacks abgearbeitet sind.
EDIT: So sollte es funktionieren:
!
var valveIDs = $('[state.id=*.VALVE_STATE](functions=Ventil_Anbau)'); ! valveIDs.on(function (obj) { var MaxValve=-1; var j = 0; valveIDs.each(function (id, i) { getState(id, function (error,oID) { if (oID.val>MaxValve) { MaxValve = oID.val; } log(oID.val + ", "+ MaxValve); j++; if (j === valveIDs.length) { log ("Maximal: " + MaxValve); // Weiterer Code, der zum Abschluss der Prüfung ausgeführt wird } }); }); }); !
-
Das wird wahrscheinlich auch nicht funktionieren, da i schneller durchgezählt wird als die callbacks abgearbeitet sind. `
Hast Recht. Dann bleibt noch außerhalb der each-Funktion eine Zählervariable zu definieren und diese bei jedem Callback-Aufruf zu erhöhen. Ist diese gleich der Array-Länge, sind wir fertig.von daher fände ich es nett, wenn es eine trickreichemöglichkeit gäbe, einen asychronen block irgendwie zu kapseln. `
Da deine asynchronen Funktionen alle auf eine gemeinsame Variable zugreifen, besteht eventuell die Gefahr einer race-condition. Das kannst du auf verschiedene Weisen umgehen.1. Indem du dir selbst eine "Funktionsschleife" baust:
<list type="decimal">4. Statt .each auf dem ersten Array-Element getState aufrufen.-
Im Callback das erste Element aus dem Array entfernen.
-
Ist das Array nun leer, bist du fertig. Sonst zurück zu 1.
Das Prinzip (ohne getState) wird z.B. im zwave-Adapter angewendet (funktion doFix) https://github.com/ioBroker/ioBroker.zw … in.js#L635
Finde ich persönlich nicht so schön, da die eigentliche Aufgabe im Loop-Konstrukt verwaschen wird. Ich bevorzuge Promises:
2. Mit Promises arbeiten
Dazu musst du eine promisifizierte Version der getState-Funktion erstellen, nennen wir sie mal getStateP. Geht z.B. mit dieser Helfer-Funktion:
(Ab NodeJS 6) https://github.com/AlCalzone/ioBroker.t … ises.js#L6
(NodeJS 4) https://github.com/AlCalzone/ioBroker.g ... ises.js#L3
const getStateP = promisify(getState);
Dann erstellst du dir aus den State-IDs ein Array aus Promises
const getStatePromises = valveIDs.map(id => getStateP(id));
Darauf wartest du nun
Promise.all(getStatePromises).then(results => { // results ist ein Array mit den Werten deiner States // Hier synchron weiterarbeiten const MaxValve = Math.max(results); // kein Einzelvergleich nötig! // Irgendwas mit dem Ergebnis machen }).catch(e => { // Fehler behandeln });
-
-
thnx, wieder einiges gelernt…