Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. Kann mir jemand dieses JavaScript-Konzept erklären?

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    Kann mir jemand dieses JavaScript-Konzept erklären?

    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      silmaril last edited by

      Hallo zusammen,

      ich versuche gerade, zu verstehen, wie man in JavaScript mit Funktionen arbeitet, die wiederum Funktionen als Parameter übergeben bekommen.

      Konkret geht es mir um die Funktion getHistory: https://github.com/ioBroker/ioBroker.ja … gethistory

      getHistory (instance, options, function (error, result, options, instance) {});
      

      Was ich schon meine verstanden zu haben:

      Ich kann diese Funktion aufrufen und an der Stelle, wo function(…) steht, meine eigene Funktion direkt in den Aufruf schreiben - ein recht gewöhnungsbedürftiges Konzept, aber ok.

      Innerhalb meiner Funktion kann ich dann auch auf alle Inhalte des Objekts result zugreifen, z.B. die Summe aller Werte berechnen und diese mittels console.log() ausgeben.

      Aber wie kann ich von dort aus wieder Werte an den Aufrufer von getHistory zurückgeben?

      Was ich gerne machen würde, hätte in den mir bisher geläufigen Sprachen etwa folgende Struktur:

      function DatenAusHistoryHolen(...) {
      	result = getHistory(...);
      	... weiterer Code, der aus result mein eigentliches Ergebnis berechnet ...
      	return MeinBerechnetesErgebnis;
      }
      
      

      Die derart definierte Funktion DatenAusHistoryHolen() würde ich dann gerne mehrfach aufrufen. Derzeit scheitere ich aber daran, dass ich die Ergebnisse von getHistory() zwar innerhalb der "Parameter-Funktion" nutzen kann, aber keine Syntax gefunden habe, mit der ich sie von dort aus an den Aufrufer von getHistory() zurückgeben kann.

      Entweder kann mir jetzt jemand einfach nur die richtige Syntax zeigen, mit der ich dieses Ziel erreiche, oder ich habe grundlegende Konzepte von JS noch überhaupt nicht verstanden…

      1 Reply Last reply Reply Quote 0
      • G
        gst666 last edited by

        Hallo,

        Dein Problem ist vermutlich nicht Javascript, sondern das Konzept der asynchronous callback.

        Ein klassisches Programm könnte so aussehen:

        var s=warteAufEingabe();
        console.log(s);
        ... tue sonst etwas
        
        

        Hinweis: dieses Programm ist ein Beispiel und fkt. so nicht!

        Das Programm wird sequentiell abgearbeitet. Zunächst wird auf eine Eingabe gewartet. Solange diese nicht erfolgt, ist das Programm angehalten. Erfolgt die Eingabe, wird sie ausgegeben. Alles hört sich vernünftig an. Aber, was machst Du, wenn z.B. einer den Lichtschalter drückt. Dein weiteres Programm kann nicht reagieren und hängt in der Warteschleife und wartet auf die Eingabe.

        Wie kann man das Dilemma beheben? Grundsätzlich könnte man für jedes Progrämmchen eine eigene Instanz von Javascript starten, damit diese parallel laufen. Da nodejs (das Grundgerüst von ioBroker) auch auf schwachen Rechnern laufen soll, kommt dieses Verfahren nicht in Betracht.

        Vielmehr soll das Programm nicht in einer Schleife "hängen", sondern weiter ausgeführt. Um auf das Ereignis der Eingabe reagieren zu können übergibt man eine Callback-Funktion:

        warteAufEingabe(function (s) {console.log(s);});
        ... tue sonst etwas
        

        Nach dem Aufruf der Funktion warteAufEingabe() gibt diese sofort die Kontrolle an das aufrufende Programm zurück. Es wird also anders als im ersten Programm sofort "… tue sonst etwas" ausgeführt

        Erfolgt eine Eingabe wird die Callback-Funktion aufgerufen. In dieser Funktion verarbeitest Du dann das Ergebnis der Funktion warteAufEingabe(). Hier als Beispiel eine Ausgabe auf der Konsole. Die ursprüngliche Funktion bestimmt, welche Rückgabewerte sie zur Verfügung stellt. In meinem Beispiel das Ergebnis der Eingabe. Daher übergibt warteAufEingabe() der Callback-Funktion hier genau einen Parameter. In de Deklaration der Callback-Funktion ist daher genau eine Variable angegeben (hier s). In der Funktion kannst Du s dann beliebig verarbeiten.

        Jetzt stellst Du Dir sicherlich die Frage, warum Du das nicht so formulieren kannst:

        var eingabe="leer";
        warteAufEingabe(function (s) {eingabe=s;}
        console.log(eingabe);
        ... tue sonst etwas
        
        

        Das Programm würde dann so auch tatsächlich ausgeführt. Nur das Ergebnis würde Dir nicht gefallen. Es wird als Ergebnis Dir nicht Deine Eingabe, sondern immer nur "leer" ausgeben. Aber warum? "eingabe" ist eine Globale Variable. DU setzt den Wert auch richtig in der Callback-Funktion. Jetzt kommt aber das ABER: Die Zeile "console.log(eingabe);" wird direkt nach dem Funktionsaufruf warteAufEingabe() ausgeführt. Zu diesen Zeitpunkt hast Du noch keine Eingabe gemacht. Daher hat die Variable "eingabe" immer die Wert "leer". Daher ist es wichtig die Verarbeitung in der Callback-Funktion zu machen. Falls es mal sinnvoll (oder notwendig) ist kann man das Ergebnis in einem State (mit setState nachdem man den State angelegt hat) speichern. Man kann dann mittels Ereignis per on() darauf reagieren. Wie Du siehst ist das mal wieder eine Callback-Funktion.

        Ich hoffe ich konnte Dir das Thema etwa näher bringen.

        1 Reply Last reply Reply Quote 0
        • AlCalzone
          AlCalzone Developer last edited by

          Mein Vorposter hats schon sehr gut erklärt, warum die Dinge so sind wie sie sind. Im Prinzip geht es darum, anderen Programmteilen Zeit zu geben, abzulaufen, während auf länger dauernde Prozesse gewartet wird.

          Es gibt Bestreben in JavaScript, mit dem Konstrukt async/await die äußere Form eines klassischen, synchronen Programms wiederherzustellen, während unter der Haube Callbacks hin und her ausgeführt werden. Etwa so wie du es vorhast:

          async function doSomething() {
          	var data = await getHistoryAsync(...);
          	// etwas mit data tun.
          }
          
          

          Das ist intern allerdings etwas komplizierter. Dazu müsste eine Funktion getHistoryAsync existieren, die einen Promise (Wert, der erst zu einem späteren Zeitpunkt befüllt wird) zurückgibt. Die asynchrone Funktion doSomething wird dann an dieser Stelle pausiert, anderer Code an anderer Stelle läuft ab, und wenn getHistoryAsync irgendwann mal einen Wert zurückgibt, wird doSomething dort fortgesetzt, wo sie zuvor pausiert wurde.

          Die Spezifikation hierfür ist allerdings noch nicht mal fertig und die Unterstützung nicht allzuweit fortgeschritten (z.B. erst ab NodeJS 7.10). Aktuell muss z.B. auf TypeScript oder Transpiler wie Babel zurückgegriffen werden, wenn man das verwenden will.

          1 Reply Last reply Reply Quote 0
          • S
            silmaril last edited by

            Vielen Dank für diese ausführlichen Antworten! Das wird hier langsam zu einem ausgewachsenen FAQ-Artikel 🙂

            Dass Node.js offenbar so grundlegend alles über Callbacks löst, hatte ich tatsächlich nicht erwartet. Da muss ich meine Programmiergewohnheiten doch gewaltig umstellen 😉

            Mir ist noch etwas unklar, wie man das Konstrukt, was ich gerade benötige in dieser Welt am besten umsetzt. Gibt's da vielleicht ein passendes Design-Pattern?

            Ich möchte eine Funktion haben, die mittels getHistory ein Zeitintervall eines Objektes holt und daraus Zwischenergebnisse berechnet.

            Diese Funktion soll im Haupt-Script (bzw. in einer anderen Funktion) mehrfach mit verschiedenen Objekt-IDs aufgerufen werden und dann aus den verschiedenen Zwischenergebnissen ein Endergebnis berechnen.

            Wie finden die Zwischenergebnisse nun am sinnvollsten ihren Weg zu dem Programmteil, der mit ihnen weiterrechnet?

            Muss ich für jedes Zwischenergebnis einen State anlegen?

            Kann der Datentyp eines States auch ein Objekt sein?

            Kann die weiterführende Funktion auch auf die Aktualisierung mehrerer States warten, bevor sie ausgelöst wird?

            Oder könnte ich auch für jedes Zwischenergebnis eine globale Variable in meinem Script anlegen und diese by-Reference übergeben?

            Gibt es sowas in JS überhaupt? (In C wäre das einfach ein Pointer auf die Variable.)

            1 Reply Last reply Reply Quote 0
            • AlCalzone
              AlCalzone Developer last edited by

              @silmaril:

              Ich möchte eine Funktion haben, die mittels getHistory ein Zeitintervall eines Objektes holt und daraus Zwischenergebnisse berechnet.

              Diese Funktion soll im Haupt-Script (bzw. in einer anderen Funktion) mehrfach mit verschiedenen Objekt-IDs aufgerufen werden und dann aus den verschiedenen Zwischenergebnissen ein Endergebnis berechnen.

              Wie finden die Zwischenergebnisse nun am sinnvollsten ihren Weg zu dem Programmteil, der mit ihnen weiterrechnet? `
              Off the top of my head:

              Funktion A erstellt ein Array von "Aufgaben" und ein leeres Array, in dem später die "Ergebnisse" gespeichert werden.

              Funktion B erwartet ebendieses Array von Aufgaben und das Ergebnis-Array:

              • Wenn beide gleich lang sind, ruft sie Funktion C mit den Ergebnissen auf

              • Wenn nicht, ruft sie getHistory mit der nächsten "Aufgabe" (Aufgabe[Ergebnisarray.length]) auf. Der Callback fügt das Ergebnis ans Ergebnisarray an und ruft dann Funktion B wieder auf.

              Funktion C macht dann mit den Ergebnissen weiter.

              –

              Oder du verwendest TypeScript/Babel und erledigst alles in einer Async-Funktion, dazu muss aber ein Promise-Wrapper für getHistory geschrieben werden:

              • Objekt-ID-Array in ein Array von Promises für die getHistory-Rückgabewerte transformieren

              • var ergebnisse = await Promise.all(promiseArray);

              • mit den Ergebnissen weiterrechnen.

              1 Reply Last reply Reply Quote 0
              • AlCalzone
                AlCalzone Developer last edited by

                @silmaril:

                Muss ich für jedes Zwischenergebnis einen State anlegen? `
                Muss/sollte kein State sein, Variablen tun es auch.

                @silmaril:

                Kann der Datentyp eines States auch ein Objekt sein? `
                Jein. Du kannst per JSON.stringify ein Objekt in einen String wandeln und diesen speichern. Später dann per JSON.parse wieder zurückwandeln. Ist aber nicht besonders elegant.

                @silmaril:

                Kann die weiterführende Funktion auch auf die Aktualisierung mehrerer States warten, bevor sie ausgelöst wird? `
                Nicht ohne weiteres, würde wie gesagt keine States verwenden für Zwischenergebnisse

                @silmaril:

                Oder könnte ich auch für jedes Zwischenergebnis eine globale Variable in meinem Script anlegen und diese by-Reference übergeben? `
                Objekte werden prinzipiell by ref übergeben, boolean, string, number, etc. by value. Warum muss es byRef sein?

                Von "zu vielen" globalen Variablen ist abzuraten, ist aber möglich. Mit sauber definierten Funktionen und -argumenten ist das nicht unbedingt nötig.

                1 Reply Last reply Reply Quote 0
                • S
                  silmaril last edited by

                  Zur Art und Weise wie JS Variablen übergibt habe ich noch was hübsches gefunden: https://stackoverflow.com/questions/660 … s-by-value

                  "Object Sharing" ist wohl der korrekte Fachbegriff und technisch gesehen fasst dieser Satz für mich alles zusammen:

                  "Javascript is always pass by value, but when a variable refers to an object (including arrays), the "value" is a reference to the object. "

                  Wenn ich das richtig verstanden habe, heißt das ich muss mir nur ein Objekt anlegen, und dieses meiner Funktion übergeben, die ihre Ergebnisse dort hineinschreibt. Danach stehen die Werte auch an der ursprünglichen Stelle zur Verfügung.

                  Hoffentlich finde ich am Wochenende die Zeit, das mal ausführlich auszuprobieren und dann etwas schlauer mit der nächsten Frage hier aufzuschlagen 😉

                  Die Nebel lichten sich langsam - danke!

                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post

                  Support us

                  ioBroker
                  Community Adapters
                  Donate

                  986
                  Online

                  31.7k
                  Users

                  79.6k
                  Topics

                  1.3m
                  Posts

                  3
                  7
                  1589
                  Loading More Posts
                  • Oldest to Newest
                  • Newest to Oldest
                  • Most Votes
                  Reply
                  • Reply as topic
                  Log in to reply
                  Community
                  Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                  The ioBroker Community 2014-2023
                  logo