NEWS
[Neu] Diverse async-Funktionen im JavaScript-Adapter
-
Update: Ich dürfte einen Fehler gemacht haben. Das Beispiel von unten ist zu einfach. Da dürfte es zu keinem Problem kommen. Aber Prinzipiell gibt es das Problem.
Ist eigentlich jemand von euch schon auf "Concurrency"-Probleme gestoßen? JavaScript ist ja Singlethreaded, sodass man sich normalerweise nicht um Concurrency kümmern muss.
Aber mit async/await kann es leicht passieren, dass derselbe Code scheinbar "gleichzeitig" ausgeführt wird.
Aktuelles Beispiel:
Ich hole mir mit einem HTTP-Request einige Status-Werte eines Gerätes. Ich hab das etwas abstrahiert und gecached. D.h. ich sage sowas wie:getValue('X')
. Wenn der Werte vom letzten Abruf noch nicht zu alt ist, liefert mirgetValue
den direkt, sonst werden die Werte zuvor per HTTP-Request neu geladen.let x = await getValue(`X`); let y = await getValue(`Y`);
Schaut unscheinbar aus
Aber wenn der Cache nicht mehr gültig ist, dann werden hier zwei HTTP-Requests gemacht. Während nämlichgetValue('X')
auf den HTTP-Response wartet, wirdgetValue('Y')
ausgeführt und startet dann ebenfalls den HTTP-Request.Das Ganze würde natürlich mit Callbacks genauso passieren.
Etwas Änliches kann auch mit set/getStateAsync passieren. Auch beim "alten"
setState
. Da aber das "alte"getState
blockiert, stolpert man da nicht so häufig drüber.Leider hat JavaScript zwar async/await aber - soweit ich weiß - keinen eingebauten Locking-Mechanismus (Locks, Semaphore oder Ähnliches.)
Es gibt ein paar NPM-Module. Hat da schon jemand Erfahrung, was da sinnvoll ist?
-
@noox sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Während nämlich getValue('X') auf den HTTP-Response wartet, wird getValue('Y') ausgeführt und startet dann ebenfalls den HTTP-Request.
Genau das verhindert
await
eigentlich (wenn die Funktion entweder async ist oder einen Promise zurück gibt). Ich gehe schwer davon aus, dass deinegetValue
-Funktion nicht richtig implementiert ist.@noox sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Etwas Änliches kann auch mit set/getStateAsync passieren.
Eigentlich nicht. Zeig doch bitte mal ein Beispiel, wo das nachvollziehbar ist.
-
@AlCalzone
Ja, ich hab mich verwirren lassen. Sorry. Das Beispiel ist zu einfach.Passieren würde es aber meiner Meinung nach, wenn die Zugriffe unabhängig voneinander wären. Also z.B. einer von einer Subscription oder einem Timer (setTimeout) aus. Und in meinem Fall war es ein setTimeout.
Aber selbst da befürchte ich, dass ich mich verschaut habe, da ich zwei Geräte parallel abfrage.
Aber ich hatte früher schon mal das Problem, wo ein Script einen State schreibt, und ein anderes diesen abonniert hat. Und wo dann kurzzeitig der State öfter geändert wurde, als ihn die Subscription abarbeiten konnte. Damals habe ich es ohne Lock gelöst, aber sowas könnte ein Fall für Locks sein.
Aber auch deswegen war ich diesmal etwas zu vorschnell. Sorry! -
Ui ... und ich hab mehrmals das
await
vergessen. Ich programmiere in Visual Studio Code.Bin's aber von C# und Visual Studio so gewohnt, dass man auf ein vergessenes
await
aufmerksam gemacht wird. -
@noox sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Bin's aber von C# und Visual Studio so gewohnt, dass man auf ein vergessenes await aufmerksam gemacht wird.
Das macht VSCode auch, wenn die Einstellungen entsprechend gesetzt sind (Typechecking aktiv)
-
@noox sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Passieren würde es aber meiner Meinung nach, wenn die Zugriffe unabhängig voneinander wären. Also z.B. einer von einer Subscription oder einem Timer (setTimeout) aus. Und in meinem Fall war es ein setTimeout.
Korrekt. Da kann dir aber kein so grundlegendes Sprachfeature helfen, das musst du selbst steuern.
-
Hallo Zusammen,
ich übe mich auch gerade im Erstellen (und löschen) von Datenpunkten. Das Beispiel hier läuft ja grundsätzlich wunderbar. Da in meinem Projekt die Datenpunkte zur Laufzeit variieren, müssen die Datenpunkte auch wieder gelöscht werden. Dazu habe ich nun eine zweite Routine erstellt, mit der ich die Datenpunkte auch löschen kann. Danach sollten die Datenpunkte wieder neu erstellt werden. Öfters kommt es jedoch vor, dass dererste
Datenpunkt im Admin/Objekte nicht angezeigt wird. Mir ist ja bekannt, dass manchmal im Admin die Objekte je nach Browser nicht immer direkt sauber dargestellt werden, sondern erst nach einem refresh.
Ich hab meinen Test so gestaltet, dass ich durch einen Datenpunkt die Abfolge Löschen/Neu setzen immer wieder ausgelöst habe. Ich hatte ja gelesen, dass es per se mit einem Datenpunkt '0' nicht geht. Habe ich noch irgend etwas übersehen oder bin ich mit meinem Vorhaben zum scheitern verurteilt.
-
@rene55 bitte nutze den code tag für scripte - sonst nicht gut lesbar
https://forum.iobroker.net/post/394912
kannst du auch im spoiler-tag zusätzlich zufügen
-
@liv-in-sky Hab ich das so richtig in Code Tags gesetzt?
-
Hi Leute,
kleines Problem. Ich hab das in meinem neuen Skript auch mal versucht mit Datenpunkt anlegen und gleich auslesen (brauch ich zwar eigentlich nicht weil die Werte beim anlegen schon bekannt sind, aber ich wollts mal ausprobieren). Leider klappt das mal und mal nicht. Der Code is nahezu identisch mit dem Beispiel, und wird aufgerufen durch nen Trigger sobald das Object "Awtrix" befüllt wurde, und sieht so aus:async function CreateDps() { //Wird durch Trigger aufgerufen sobald get settings ein Ergebnis liefert log("Reaching CreateDps"); try { for (let x in Awtrix) { //Alle properties durchgehen const id = praefix + ".Settings." + x; if (await existsStateAsync(id)) { if (logging) log(`State ${id} already exists, nothing to do`, 'info'); } else { await createStateAsync(id, { name: x, type: typeof Awtrix[x], read: true, write: true, def: Awtrix[x] }); const stateObject = await getStateAsync(id); if (stateObject && stateObject.val) { log(`State '${id}' created, value: '${stateObject.val}'`) } else { log(`Unable to get state value of '${id}'.`, 'error'); } } } InitInProgress = false; } catch (error) { log(`Unexpected error - ${error}`, 'error'); } }
Und was passiert ist dann das:
Tatsächlich angelegt wurden die States samt Inhalt aber (am Ende gekürzt):
Was ist, bzw. mache ich falsch?
-
@pittini sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
if (stateObject && stateObject.val) {
du möchtest prüfen ob ein Wert existiert, prüfst aber ob der Wert true bzw >0 bzw != "" ist
-
@fastfoot sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
@pittini sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
if (stateObject && stateObject.val) {
du möchtest prüfen ob ein Wert existiert, prüfst aber ob der Wert true bzw >0 bzw != "" ist
Hm, ok, danke, ich ging davon aus das @Mic schon weis was er schreibt. Das ist 1:1 aus dem Beispiel. Mal guggen obs wirklich daran liegt.
-
@pittini sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Hm, ok, danke, ich ging davon aus das @Mic schon weis was er schreibt.
wusste er Seine state values fangen im Beispiel bei 1 an
oder anders ausgedrückt: Wenn du deinen Wert prüfen willst, nimm
if (stateObject && stateObject.val == Awtrix[x]) {
-
@fastfoot sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
@pittini sagte in [Neu] Diverse async-Funktionen im JavaScript-Adapter:
Hm, ok, danke, ich ging davon aus das @Mic schon weis was er schreibt.
wusste er Seine state values fangen im Beispiel bei 1 an
oder anders ausgedrückt: Wenn du deinen Wert prüfen willst, nimm
if (stateObject && stateObject.val == Awtrix[x]) {
- Du hattest recht.
- Hab ich das zum testen grad so gemacht, das klappt auch:
if (stateObject && typeof stateObject.val != "undefined") {
- Danke.