NEWS
async / await -> Muster Anleitung gesucht
-
@hendrick Da ist ja wieder ein Sleep-Befehl? Oder ist der in dem Fall Kosmetik? Dem TE ging es ja darum eben diese nicht mehr zu benötigen, oder habe ich da etwas falsch verstanden?
Ich würde die await Funktion auch sehr gerne nutzen, habe mich aber noch nicht weiter damit beschäftigt. -
@bahnuhr vielleicht könntest du mal die einzelnen funktionen hier zeigen oder dein existierendes script posten. dann wäre es einfacher, ein muster zu erstellen. so könnte man dann jeden deiner punkte als funktion darstellen und das ganze schrittweise aufbauen - nicht jeder hat ein yamaha system und weiß, wie das zu steuern ist
-
Das Await wartet nicht bis etwas fertig ist, sondern bis eine Funktion vollständig durchgelaufen ist. Was du also brauchst sind Funktionen die erst zurück kommen bis die dazu gehörige Funktion erledigt ist. Dieses ist die Schlüsselaufgabe, die mit async/await erst einmal nichts zu tun hat. Insbesondere das mp3 erstellen und kopieren muss entsprechend abgedeckt sein.
Diese solltest du als
async
definieren, dann kannst du diese mitawait
aufrufen.A.
-
@hendrick nicht ganz
// funktion aufrufen await test(); // warte bis die ganze funktion fertig ist async function test() { try { await setStateAsync('PFAD ZUM DATENPUNKT', {val:true, ack:false}); // warte bis setStateAsync meldet ist durch await setStateAsync('PFAD ZUM DATENPUNKT', {val:true, ack:false}); // warte bis setStateAsync meldet ist durch let res = await setStateAsync('PFAD ZUM DATENPUNKT', {val:true, ack:false}); // warte bis setStateAsync meldet ist durch und gibt das ergebnis in die res variable } catch (error) { return; } }
man kann es noch mehr aufbohren und einen return wert ausgeben... und und und
-
@liv-in-sky sagte in async / await -> Muster Anleitung gesucht:
existierendes script posten
Na klar kann ich dies machen; ist aber sehr komplex und auf mich abgestimmt.
Erklärung:
In den Zeilen 77-79 werden per timeout die einzelnen Funktionen aufgerufen.
90 ff.: Hier wird die Datei kopiert
114 ff: Hier wird geprüft ob das richtige "Verzeichnis" auf dem yamaha Lautspreche per api eingestellt ist.
141 ff: Unterprogramm um ein Verzeichnis zu setzen
160 ff: Hier wird die erstellte mp3 dann abgespielt
173 ff: Dateien werden wieder gelöschtmfg
Dieter -
@asgothian sagte in async / await -> Muster Anleitung gesucht:
Diese solltest du als async definieren, dann kannst du diese mit await aufrufen.
Danke für die Info. Aber so richtig verstanden habe ich dies nicht.
Nach meinem Verständnis muss das System doch zurück geben: Dateien sind kopiert oder mp3 ist erstellt.
Also so ein return "erledigt".Nur wie ?
-
@bahnuhr sagte in async / await -> Muster Anleitung gesucht:
ist aber sehr komplex und auf mich abgestimmt
daher gibt es ja auch kein allgemeines muster - jedes script ist anders
-
-
@bahnuhr sagte in async / await -> Muster Anleitung gesucht:
Aber so richtig verstanden habe ich dies nicht.
Hilft das?
https://gist.github.com/AlCalzone/d14b854b69ce5e8a03718336cc650a95 -
@alcalzone sagte in async / await -> Muster Anleitung gesucht:
@bahnuhr sagte in async / await -> Muster Anleitung gesucht:
Aber so richtig verstanden habe ich dies nicht.
Hilft das?
https://gist.github.com/AlCalzone/d14b854b69ce5e8a03718336cc650a95Ja, kenn ich.
Hab ich schon mehrmals gelesen. Ist aber für mich "starker toback".mfg
Dieter -
@liv-in-sky sagte in async / await -> Muster Anleitung gesucht:
die funktion "Datei_kopieren" finde ich nicht im script - ist die unter global ?
Ja, ist ein globales Script.
anbei:function Datei_kopieren(von, nach) { // Pfad immer mit / darstellen // als "von" und "nach" den genauen Pfad angeben, z.B. "opt/iobroker/iobroker-data/files/vis.0/Daten/test.txt" var fs = require('fs'); const datei= fs.readFileSync(von); setTimeout(function(){ fs.writeFileSync(nach, datei); }, 500); }
-
@htrecksler said in async / await -> Muster Anleitung gesucht:
Da ist ja wieder ein Sleep-Befehl? Oder ist der in dem Fall Kosmetik?
Das war nur Kosmetik, aber war in diesem Fall wohl verwirrend, denn die sleep braucht es nicht.
@bahnuhr said in async / await -> Muster Anleitung gesucht:
function Datei_kopieren(von, nach)
Lass mal mit dieser Funktion anfangen. Diese muss auf async/await umgeschrieben werden.
Hier ein komplettes Beispiel:
const fs = require('fs'); mainAsync(); async function mainAsync() { try { /** * Hier kopieren wir die Datei. */ log(`Trying to copy file...`); const fileSource = '/opt/iobroker/_test1/pic.png'; const fileTarget = '/opt/iobroker/_test1/pic-copy.png'; const fileCopied = await copyFileAsync(fileSource, fileTarget); if (!fileCopied) { // Beim Kopieren trat ein Fehler auf. log(`Datei '${fileSource}' konnte nicht nach '${fileTarget}' kopiert werden.`, 'warn'); log(`Script wird beendet.`, 'warn'); return false; } log(`File copied.`); /** * Datei erfolgreich kopiert, hier geht's weiter */ // HIER GEHT ES WEITER // ... // Setze z.B. jetzt einen Datenpunkt // await setStateAsync('0_userdata.0.test.123.Datei-ist-kopiert', {val:true, ack:false}); } catch (error) { dumpError(`[mainAsync()]`, error); return false; } } /** * Copy file async - https://stackoverflow.com/a/30405105 * @param {string} source /path/to/file * @param {string} target /path/to/file * @return {Promise<boolean>} true if successful, false if not */ async function copyFileAsync(source, target) { const rd = fs.createReadStream(source); const wr = fs.createWriteStream(target); try { await new Promise( (resolve, reject) => { rd.on('error', reject); wr.on('error', reject); wr.on('finish', resolve); rd.pipe(wr); }); return true; } catch (error) { rd.destroy(); wr.end(); dumpError(`[copyFileAsync()]`, error); return false; } } /** * Error Message to Log. Handles error object being provided. * @param {string} msg - (intro) message of the error * @param {*} [error=undefined] - Optional: Error object or string */ function dumpError(msg, error=undefined) { if (!error) { console.error(msg); } else { if (typeof error === 'object') { if (error.stack) { log(`${msg} – ${error.stack}`, 'error'); } else if (error.message) { log(`${msg} – ${error.message}`, 'error'); } else { log(`${msg} – ${JSON.stringify(error)}`, 'error'); } } else if (typeof error === 'string') { log(`${msg} – ${error}`, 'error'); } else { log(`[dumpError()] : wrong error argument: ${JSON.stringify(error)}`, 'error'); } } }
Zur Erklärung:
In der FunktionmainAsync()
rufst du nach und nach all deine asynchronen Funktionen etc. auf.
Im Beispiel rufen wir also zunächstcopyFileAsync()
auf. Sobald erfolgreich kopiert, kannst du dann weitere Funktionen aufrufen, wie z.B. mitawait setStateAsync()
einen Datenpunkt setzen, usw. -
@bahnuhr said in async / await -> Muster Anleitung gesucht:
Habt ihr mal ein Muster mit diesem await, das halt wartet bis etwas erledigt ist, und dann zum nächsten await geht.
@htrecksler said in async / await -> Muster Anleitung gesucht:
Ich würde die await Funktion auch sehr gerne nutzen, habe mich aber noch nicht weiter damit beschäftigt.
Ich hatte auch einige Zeit gebraucht, um die async/await Logik zu verstehen
Oben hab ich schon mal versucht, zu erklären.
Weiteres Beispiel:
In einem Script will ich Datenpunkte anlegen, und danach diese auf Änderung überwachen. Die Überwachung auf Änderung soll aber nur stattfinden, nachdem die Datenpunkte angelegt sind, was mehrere Millisekunden dauern kann mitcreateState()
. Daher bietet sich hier super async/await an.mainAsync(); async function mainAsync() { try { await createStateAsync('0_userdata.0._0_TEST.STATE_1', {name:'Test 1', type:'string', read:true, write:true, role:'state', def:'neu angelegt' }); await createStateAsync('0_userdata.0._0_TEST.STATE_2', {name:'Test 2', type:'boolean', read:false, write:true, role:'button', def:false }) // States erstellt, jetzt machen wir Subscribe on({id: ['0_userdata.0._0_TEST.STATE_1', '0_userdata.0._0_TEST.STATE_2'], change:'any', ack:false}, async (obj) => { log(`State '${obj.id}' changed to [${obj.state.val}]`) }); } catch (error) { dumpError(`[mainAsync()]`, error); return false; } } /** * Error Message to Log. Handles error object being provided. * @param {string} msg - (intro) message of the error * @param {*} [error=undefined] - Optional: Error object or string */ function dumpError(msg, error=undefined) { if (!error) { console.error(msg); } else { if (typeof error === 'object') { if (error.stack) { log(`${msg} – ${error.stack}`, 'error'); } else if (error.message) { log(`${msg} – ${error.message}`, 'error'); } else { log(`${msg} – ${JSON.stringify(error)}`, 'error'); } } else if (typeof error === 'string') { log(`${msg} – ${error}`, 'error'); } else { log(`[dumpError()] : wrong error argument: ${JSON.stringify(error)}`, 'error'); } } }
-
Danke erst einmal von mir.
Ich muss mir dies mal anschauen.Wenn noch jemand Beispiele hat nur her damit.
-
@bahnuhr said in async / await -> Muster Anleitung gesucht:
Wenn noch jemand Beispiele hat nur her damit.
Melde dich einfach, wenn du wo hängst. Ich denke das mit createStateAsync() ist ein gutes Beispiel.
Du kannst das auch in eine Funktion kapseln, also:
mainAsync(); async function mainAsync() { try { const createStateResult = await createSomeStates(); if (createStateResult) { log('States erfolgreich erstellt, wir machen weiter.'); } else { log('Fehler bei State-Erstellung. Wir brechen ab', 'warn'); return; } // States erstellt, jetzt machen wir Subscribe on({id: ['0_userdata.0._0_TEST.STATE_1', '0_userdata.0._0_TEST.STATE_2'], change:'any', ack:false}, async (obj) => { log(`State '${obj.id}' changed to [${obj.state.val}]`) }); } catch (error) { dumpError(`[mainAsync()]`, error); return false; } } /** * @return {Promise<boolean>} */ async function createSomeStates() { try { await createStateAsync('0_userdata.0._0_TEST.STATE_1', {name:'Test 1', type:'string', read:true, write:true, role:'state', def:'neu angelegt' }); await createStateAsync('0_userdata.0._0_TEST.STATE_2', {name:'Test 2', type:'boolean', read:false, write:true, role:'button', def:false }) return true; } catch (error) { dumpError(`[createSomeStates()]`, error); return false; } } /** * Error Message to Log. Handles error object being provided. * @param {string} msg - (intro) message of the error * @param {*} [error=undefined] - Optional: Error object or string */ function dumpError(msg, error=undefined) { if (!error) { console.error(msg); } else { if (typeof error === 'object') { if (error.stack) { log(`${msg} – ${error.stack}`, 'error'); } else if (error.message) { log(`${msg} – ${error.message}`, 'error'); } else { log(`${msg} – ${JSON.stringify(error)}`, 'error'); } } else if (typeof error === 'string') { log(`${msg} – ${error}`, 'error'); } else { log(`[dumpError()] : wrong error argument: ${JSON.stringify(error)}`, 'error'); } } }
-
@hendrick
Hallo,
habe dein Script mal probiert mit einer ts Datei, Größe 480 MB
Und ein paar logs eingebaut.const fs = require('fs'); log ("start"); begin(); function begin() { mainAsync(); log ("Punkt 1") } async function mainAsync() { try { /** * Hier kopieren wir die Datei. */ log(`Trying to copy file...`); const fileSource = "/opt/iobroker/iobroker-data/files/vis.0/James.ts"; const fileTarget = "/mnt/Jam.ts"; const fileCopied = await copyFileAsync(fileSource, fileTarget); if (!fileCopied) { // Beim Kopieren trat ein Fehler auf. log(`Datei '${fileSource}' konnte nicht nach '${fileTarget}' kopiert werden.`, 'warn'); log(`Script wird beendet.`, 'warn'); return false; } log(`File copied.`); await weiter(); /** * Datei erfolgreich kopiert, hier geht's weiter */ // HIER GEHT ES WEITER // ... // Setze z.B. jetzt einen Datenpunkt // await setStateAsync('0_userdata.0.test.123.Datei-ist-kopiert', {val:true, ack:false}); } catch (error) { dumpError(`[mainAsync()]`, error); return false; } } function weiter(){ log ("Punkt 2") } /** * Copy file async - https://stackoverflow.com/a/30405105 * @param {string} source /path/to/file * @param {string} target /path/to/file * @return {Promise<boolean>} true if successful, false if not */ async function copyFileAsync(source, target) { const rd = fs.createReadStream(source); const wr = fs.createWriteStream(target); try { await new Promise( (resolve, reject) => { rd.on('error', reject); wr.on('error', reject); wr.on('finish', resolve); rd.pipe(wr); }); return true; } catch (error) { rd.destroy(); wr.end(); dumpError(`[copyFileAsync()]`, error); return false; } } /** * Error Message to Log. Handles error object being provided. * @param {string} msg - (intro) message of the error * @param {*} [error=undefined] - Optional: Error object or string */ function dumpError(msg, error=undefined) { if (!error) { console.error(msg); } else { if (typeof error === 'object') { if (error.stack) { log(`${msg} – ${error.stack}`, 'error'); } else if (error.message) { log(`${msg} – ${error.message}`, 'error'); } else { log(`${msg} – ${JSON.stringify(error)}`, 'error'); } } else if (typeof error === 'string') { log(`${msg} – ${error}`, 'error'); } else { log(`[dumpError()] : wrong error argument: ${JSON.stringify(error)}`, 'error'); } } }
Hiersieht man, dass "file copied" und "Punkt 2" schon nach 4 Sek. kommt.
Wenn ich dann aber im win.Explorer nachschaue wird die Datei noch kopiert (MB zählt nach oben).
Folglich scheint die Kopie doch noch nicht fertig zu sein.
Oder ich versteht dies falsch und hab irgendwo einen Denkfehler.mfg
Dieter -
Gute Frage.
Die async/await-Abarbeitung im Script ist sauber.Hier nur kleine Anmerkung:
Du ruft die Funktion "weiter()" perawait weiter();
auf, allerdings ist diese Funktion "weiter()" nicht async definiert. Hat so jetzt keine Auswirkung im Script, aber besser machst du stattfunction weiter()
einasync function weiter()
, weil du mitawait weiter();
implizierst, dass eine async Funktion aufgerufen wird, was ja so nicht der Fall ist.Jetzt aber zur Thematik:
Ich habe das jetzt nicht mit einem großen File getestet. Aber so wie sich der Code incopyFileAsync()
liest, wird mittelswr.on('finish', resolve);
erst dann ein erfolgreiches Schreiben zurückgemeldet, wenn der Prozess abgeschlossen ist.
Somit wundert es mich, dass du vorher schon die Rückmeldung bekommst.
Du könntest das mal überprüfen, in dem du im Script die Dateigröße der Quelle mit der Dateigröße des Ziels vergleichst in deiner Funktionweiter()
und diese als Log mal ausgibst. Dann weißt du, ob lt. Script wirklich kopiert wurde.Lange Rede, kurzer Sinn: dies hat nichts mit dem Script-Aufbau zu async/await zu tun, sondern könnte bei dir
fs.createWriteStream()
schon vorzeitig einfinish
liefern, obwohl noch gar nicht fertig kopiert wurde.
Seltsam, aber wäre es wert, näher einzusteigen. -
@hendrick sagte in async / await -> Muster Anleitung gesucht:
diese als Log mal ausgibst.
Hast du da mal ein Scriptschnipsel.
Danke, ich werds dann probieren.
Fazit:
Das ganze ist verdammt verwirrend und irreführend für mich.Wenn ich mir das Script so anschaue, dann sind das nur für den kopier Befehl ca. 30 Zeilen (ohne sie jetzt genau gezählt zu haben).
Mein derzeitiges kopier-Script hat 3 Zeilen + timeout Zeilen. -
@hendrick sagte in async / await -> Muster Anleitung gesucht:
Du ruft die Funktion "weiter()" per await weiter();
Ja, aber nur weil du im Beispiel den:
await setStateAsync('0_userdata.0.test.123.Datei-ist-kopiert', {val:true, ack:false});
auch mit await aufgerufen hast.Wusste nicht, dass da ein Unterschied besteht.
-
@bahnuhr said in async / await -> Muster Anleitung gesucht:
Fazit:
Das ganze ist verdammt verwirrend und irreführend für mich.
Wenn ich mir das Script so anschaue, dann sind das nur für den kopier Befehl ca. 30 Zeilen (ohne sie jetzt genau gezählt zu haben).
Mein derzeitiges kopier-Script hat 3 Zeilen + timeout Zeilen.Achtung
Ich, und wohl jeder hier, erklären dir gerne alles. Sag weiterhin gerne Bescheid, wo du konkret Fragen hast.
Viele Script-Beispiele hier im Forum sind "Quick&Dirty", aber funktionieren halt einfach
Einstieg in async/await bedeutet auch, dass du DEUTLICH besseren Quellcode bekommst. Im Idealfall verifizierst da z.T. auch mehr, und wirst mehr diszipliniert, zu prüfen.
Beispiel:
setState('XYZ', getState('ABC').val);
Hier wird einfach so ein Datenpunktwert ohne Prüfung in einen anderen Datenpunkt gesetzt. Egal, ob der DP überhaupt vorhanden ist, egal, ob der Ziel-Datenpunkt kompatibel ist zum Datentyp, etc.
Im Idealfall wartet man hier erst mal ab, ob und was der getState() überhaupt zurück liefert, und reagiert dann.
Das hat jetzt nicht unbedingt mit async/await zu tun, aber Prüfungen usw. verlängeren den Quellcode, aber ersparen dir viel Ärger in der Zukunft.
Auch die Nutzung von try/error wie in den obigen Beispielen.
JA - Quellcode wird deutlich länger. Aber auch deutlich sicherer.