NEWS
[gelöst] Dauerschleife führt zu Memoryproblemen
-
Die Idee ist, in einer Dauerschleife die Garagentoröffnung zu melden - bis es wieder zu ist. Ob das in dieser Form sinnvoll ist sei mal dahingestellt. Für den Test habe ich den Timeout auf 20 Sekunden gestellt.
Ich bin jetzt ganz neu bei ioBroker und versuche das Ganze zu verstehen. Und so möchte ich gerne verstehen, warum ich zu dieser Fehlermeldung komme:FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Der Java-Script-Code ist
var timeout; on({id: "shelly.0.SHSW-1#E8DB84D401CC#1.ext.switch1"/*Switch*/, change: "ne"}, async function (obj) { var value = obj.state.val; var oldValue = obj.oldState.val; while ((obj.state ? obj.state.val : "") == true) { timeout = setTimeout(async function () { sendTo("pushover", "send", { message: 'Lange offen', sound: "", priority: 1, title: 'Shelly Garage' }); }, 20000); } });
Das zugehörige Blockly sieht so aus.
-
-
Mit "wiederhole solange" wird eine Endlosschleife erzeugt, die regelmäßig zum Absturz führt.
Wenn alle 20 s gesendet werden soll, verwende ein Intervall, das gestoppt wird, wenn die Bedingung nicht erfüllt ist (im sonst-Zweig). -
@paul53
Das endet doch automatisch, wenn die "while"-Bedingung nicht mehr erfüllt ist.
Mein Gefühl ist, dass der von Blockly erzeugte Timout-Request nicht funktioniert, weil die Javascript-Instanz im ioBroker innerhalb weniger Sekunden (<20 Sekunden) abstürzt. Da werden schnell neue forks gestartet und der Garbage Collector kommt nicht mehr hinterher.Eine Nachricht kam auch nie an.
-
@gombersiob sagte: Das endet doch automatisch, wenn die "while"-Bedingung nicht mehr erfüllt ist.
Bis dahin ist die Schleife schon einige tausend Mal durchlaufen und ebenso viele Timer wurden erzeugt.
-
@paul53
Wenn der Timeout nicht funktioniert, hilft auch ein „sonst“-Zweig nicht - der wird ja gar nicht angesteuert, weil die Bedingung ja noch erfüllt wird. -
@gombersiob sagte: Wenn der Timeout nicht funktioniert
Der Timeout funktioniert anders, als Du wahrscheinlich annimmst: Er verzögert nicht die Ausführung der Schleife, sondern nur die Ausführung der Callback-Funktion (pushover). Wenn die Schleife verzögert werden soll, muss man Pause verwenden.
... oder ein Intervall ohne Schleife wie gezeigt.
-
@paul53
Danke, so gehts - zumindest teilweise.
Nur, der Wechsel des Status des Switches wird nicht erkannt. Ich habs nochmal so probiert - auch ohne Erfolg. Die Schleife wird nie beendet.
Im Code sieht es so aus:var switchStatus; on({id: "shelly.0.SHSW-1#E8DB84D401CC#1.ext.switch1"/*Switch*/, change: "ne"}, async function (obj) { var value = obj.state.val; var oldValue = obj.oldState.val; switchStatus = (obj.state ? obj.state.val : ""); while (switchStatus == true) { sendTo("pushover", "send", { message: 'Lange offen', sound: "", priority: 1, title: 'Shelly Garage' }); await wait(20000); switchStatus = (obj.state ? obj.state.val : ""); } console.debug('Ferdisch'); });
-
@gombersiob sagte: der Wechsel des Status des Switches wird nicht erkannt.
Getestet: Er wird erkannt, denn die Log-Ausgabe 'Ferdisch' erscheint, aber die Schleife wird nicht beendet.
Die Lösung mit dem Intervall funktioniert. -
@paul53
Bei mir habe ich die Meldung "Ferdisch" nicht gesehen. Aber wie ich jetzt feststelle, deshalb, weil ich im Protokoll keine "debug"-Meldungen angezeigt bekomme - auch wenn ich sie im Filter explizit auswähle. Ich habe den Loglevel der Meldung jetzt auf "info" gesetzt - und schon ist sie da. Und wieder Gegenprobe - sie ist wieder verschwunden. Ich werde dazu einen eigenen Topic aufmachen.Ich habe mir nun ein paar zusätzlich Debug-Meldungen erzeugt. Hier mein "Script":
Und die dazugehörigen Meldungen:
javascript.0 2022-10-02 18:29:50.374 info Stop script script.js.common.Garagentor_dauernd_offen javascript.0 2022-10-02 18:29:41.660 info script.js.common.Garagentor_dauernd_offen: setTimeout(ms=20000) javascript.0 2022-10-02 18:29:41.659 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":"","priority":1,"title":"Shelly Garage"}) javascript.0 2022-10-02 18:29:41.659 info script.js.common.Garagentor_dauernd_offen: Am Loopanfang: Switch = true javascript.0 2022-10-02 18:29:41.659 info script.js.common.Garagentor_dauernd_offen: Am Loopende: Switch = true javascript.0 2022-10-02 18:29:26.852 info script.js.common.Garagentor_dauernd_offen: Ferdisch javascript.0 2022-10-02 18:29:26.852 info script.js.common.Garagentor_dauernd_offen: Am Scriptanfang: Switch = false javascript.0 2022-10-02 18:29:26.851 info script.js.common.Garagentor: pushover: Garage zu javascript.0 2022-10-02 18:29:21.658 info script.js.common.Garagentor_dauernd_offen: setTimeout(ms=20000) javascript.0 2022-10-02 18:29:21.658 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":"","priority":1,"title":"Shelly Garage"}) javascript.0 2022-10-02 18:29:21.657 info script.js.common.Garagentor_dauernd_offen: Am Loopanfang: Switch = true javascript.0 2022-10-02 18:29:21.656 info script.js.common.Garagentor_dauernd_offen: Am Loopende: Switch = true javascript.0 2022-10-02 18:29:01.656 info script.js.common.Garagentor_dauernd_offen: setTimeout(ms=20000) javascript.0 2022-10-02 18:29:01.655 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":"","priority":1,"title":"Shelly Garage"}) javascript.0 2022-10-02 18:29:01.654 info script.js.common.Garagentor_dauernd_offen: Am Loopanfang: Switch = true javascript.0 2022-10-02 18:29:01.654 info script.js.common.Garagentor_dauernd_offen: Am Loopende: Switch = true javascript.0 2022-10-02 18:28:41.653 info script.js.common.Garagentor_dauernd_offen: setTimeout(ms=20000) javascript.0 2022-10-02 18:28:41.653 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":"","priority":1,"title":"Shelly Garage"}) javascript.0 2022-10-02 18:28:41.652 info script.js.common.Garagentor_dauernd_offen: Am Loopanfang: Switch = true javascript.0 2022-10-02 18:28:41.652 info script.js.common.Garagentor_dauernd_offen: Am Loopende: Switch = true javascript.0 2022-10-02 18:28:21.652 info script.js.common.Garagentor_dauernd_offen: setTimeout(ms=20000) javascript.0 2022-10-02 18:28:21.651 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":"","priority":1,"title":"Shelly Garage"}) javascript.0 2022-10-02 18:28:21.651 info script.js.common.Garagentor_dauernd_offen: Am Loopanfang: Switch = true javascript.0 2022-10-02 18:28:21.650 info script.js.common.Garagentor_dauernd_offen: Am Scriptanfang: Switch = true javascript.0 2022-10-02 18:28:21.649 info script.js.common.Garagentor: pushover: Garage offen
Das Script über das wir hier reden heißt "Garagentor_dauernd_offen".
In dem Log ist eine zweites Script names "Garagentor". Dieses Script teilt mit, wenn das Garagentor geöffnet wird und wan es geschlossen wird. Dabei wertet es denselben Switch-Status aus wie das andere Script.Der Loop geht zu Ende, weil, wie auch gezeigt wird, der Switch-Status auf "false" erkannt wird.
Dann wird ein neuer Loop gestartet, wie ich jetzt verstehe, erwartbar. Denn das Garagentor ist ja zu, der Switch-Status hat sich also geändert von "true" auf "false. Und bei Änderung läuft "Garagentor_dauernd_offen" eben wieder an.
Merkwürdig ist aber, dass es den Switch-Status als "true" erkennt. Ich schaue unter "Objekte" nach - stimmt nicht. Wenn das so wäre müsste ja auch "Garagentor" wieder laufen.
Welches Objekt wird da ausgewertet?Die Idee mit dem Intervall will ich gerne auch mal probieren. Aber mir geht es hier in erster Linie ja darum meine Fehler zu verstehen.
-
@gombersiob
Ich habe mir den Log nochmal sorgfältig angeschaut und dabei fällt noch was auf. Ich gebe während des Laufes des Scriptes "drei" Meldungen aus:- Status am Scriptanfang
- Status am Loopanfang
- Status am Loopende
Diese Meldungen gibt es:
- Tor auf
- (1) Status am Scriptanfang: true]
- (1) Status am Loopanfang: true
- (1) Status am Loopende: true]
.... - Tor zu
- (2) Status am Scriptanfang: false
- (1) Ferdisch
- (2) Status am Loopanfang: true
- (2) Status am Loopende: true
mit den Nummern in Klammern vor der Meldung bezeichne ich die Nummer des Scripteslaufes, der die Meldung (nach meinem Verständnis) bringt.
Auf die Meldung "Ferdisch" kann man nur kommen, wenn der Loop verlassen wird. Das geschieht logischerweise nur, wenn die while-Bedingung nicht erfüllt wird, der Switch-Status "false" also erkannt wurde.
Beim zweiten Start wird am Scriptanfang dieser Status auch so angezeigt. Aber im Loop dann nicht mehr.
-
@gombersiob
Habe das Ganze jetzt mit "Intervall" probiert.
Mit demselben Ergebnis.
javascript.0 2022-10-04 20:33:17.525 info Stop script script.js.common.Garagentor_dauernd_offen admin.0 2022-10-04 20:33:15.387 info ==> Connected system.user.admin from 2001:9e8:237e:7700:9d61:216e:c513:f2d5 javascript.0 2022-10-04 20:33:05.405 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":""}) javascript.0 2022-10-04 20:33:05.404 info script.js.common.Garagentor_dauernd_offen: Status zu Loopanfang: true javascript.0 2022-10-04 20:32:45.405 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":""}) javascript.0 2022-10-04 20:32:45.404 info script.js.common.Garagentor_dauernd_offen: Status zu Loopanfang: true javascript.0 2022-10-04 20:32:25.404 info script.js.common.Garagentor_dauernd_offen: clearInterval() => cleared javascript.0 2022-10-04 20:32:13.726 info script.js.common.Garagentor_dauernd_offen: Ferdisch mit Status: false javascript.0 2022-10-04 20:32:13.726 info script.js.common.Garagentor_dauernd_offen: setInterval(ms=20000) javascript.0 2022-10-04 20:32:13.725 info script.js.common.Garagentor: pushover: Garage zu javascript.0 2022-10-04 20:32:05.403 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":""}) javascript.0 2022-10-04 20:32:05.403 info script.js.common.Garagentor_dauernd_offen: Status zu Loopanfang: true javascript.0 2022-10-04 20:31:45.403 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":""}) javascript.0 2022-10-04 20:31:45.403 info script.js.common.Garagentor_dauernd_offen: Status zu Loopanfang: true javascript.0 2022-10-04 20:31:25.403 info script.js.common.Garagentor_dauernd_offen: sendTo(adapter=pushover, cmd=send, msg={"message":"Lange offen","sound":""}) javascript.0 2022-10-04 20:31:25.402 info script.js.common.Garagentor_dauernd_offen: Status zu Loopanfang: true javascript.0 2022-10-04 20:31:05.401 info script.js.common.Garagentor_dauernd_offen: Ferdisch mit Status: true javascript.0 2022-10-04 20:31:05.401 info script.js.common.Garagentor_dauernd_offen: setInterval(ms=20000)
Das Script endet wenn die garage zu ist, startet aber wieder weil dadurch der Trigger geändert wurde und erkennt den Trigger-Status falsch - ebenso wie mit "Pause".
Für Anregungen wäre ich sehr dankbar.
-
@gombersiob sagte: Für Anregungen wäre ich sehr dankbar.
-
Ich sehe den Unterschied im Code. Bei Ihrem Vorschlag müsste ich die „if..then“- Abfrage zweimal machen, im Loop nochmal um das Ende erkennen zu können. Sowas finde ich hässlich.
Es mag sein, dass das funktioniert, ich probiere es morgen nochmal aus.
Aber für mich wäre es logisch nicht erklärbar wieso das funktioniert und das andere nicht. Wieso wird der Status außerhalb des Loops richtig und innerhalb nicht richtig erkannt? -
@gombersiob sagte: Ich sehe den Unterschied im Code.
Wirklich? Weshalb sieht es dann immer noch so eigenartig aus? Ist das so schwer nachzubauen?
-
Ich verstehe die Frage nicht.
In der Schleife muss das Ende doch geprüft werden sonst läuft sie endlos. Der „stop“ der zyklischen Ausführung in Ihrem Code ist überflüssig. Wenn der „sonst“-Zweig aufgerufen wird, gibt es ja keinen Loop. -
@gombersiob sagte: Der „stop“ der zyklischen Ausführung in Ihrem Code ist überflüssig.
Ach ja? Teste es wie gezeigt - dann reden wir weiter.
-
@paul53
Sie haben recht - es funktioniert tatsächlich. Da stellen sich 40 Jahre Programmiererfahrung doch gleich in Frage. In keiner anderen Programmiersprache, die ich kenne, hätte das so funktioniert. JavaScript habe ich bisher nicht programmiert, nur gelesen. Aber sowas auch noch nicht. Es kommt mir reichlich merkwürdig vor. Ist das eine eigene ioBroker-JavaScript Implementation oder ist das Sprachstandard?var switchStatus, Intervall; on({id: "shelly.0.SHSW-1#E8DB84D401CC#1.ext.switch1"/*Switch*/, change: "ne"}, async function (obj) { var value = obj.state.val; var oldValue = obj.oldState.val; switchStatus = (obj.state ? obj.state.val : ""); console.log(('Scriptanfang Status: ' + String(switchStatus))); if (switchStatus == true) { Intervall = setInterval(async function () { console.log(('Loopanfang Status: ' + String(switchStatus))); switchStatus = (obj.state ? obj.state.val : ""); }, 20000); } else { console.log(('Else Status: ' + String(switchStatus))); (function () {if (Intervall) {clearInterval(Intervall); Intervall = null;}})(); } });
Bleibt die Frage, warum die Stati in meinen anderen Versuchen falsch ausgewertet wurden. Ich danke für diese Lösung - bleibe aber verwirrt zurück.
-
@gombersiob sagte: ist das Sprachstandard?
Ja, Javascript arbeitet nicht zyklisch, sondern ereignisgesteuert.
-
@paul53
Ich kann mir programmlogisch kein Ereignis vorstellen bei dem eine Routine aus dem "if-then"-Zweig in den "else"-Zweig wechselt. In den "else"-Zweig kommt man nur über "if" (genauso wie in den "then"-Zweig"). Aber wenn man einmal in einem der beiden Zweige ist kommt man nicht mehr über "if". Der Ablauf bleibt mir ein Rätsel.