Hallo effel,
leider ist meine Überarbeitung von Promises im Wiki noch nicht fertig, aber vielleicht hilft Dir der aktuelle Stand auch schon weiter.
Kurz gesagt: Die import-Funktion gibt ein Promise zurück, das mit dem Modul-Objekt erfüllt wird. Dieses Objekt wird dann an die Funktion übergeben, die .then() als Parameter erhält.
Das Problem: .then() wird aufgerufen, bevor das Promise erfüllt ist. Wenn Du also schreibst:
let modul = import("./mymodule.js")
.then(modul.f1(a,b,c));
dann gibt es hier gleich mehrere Probleme.
(1) An modul
wird nicht das Modul zugewiesen, sondern ein Promise. Und zwar nicht das, was von import() zurückkommt, sondern das, das von then() zurückkommt.
(2) Wenn then() so funktionierte, wie Du Dir das vorstellst, dann würdest Du an then() nicht die Funktion im Modul übergeben, sondern ihr Ergebnis. Das ist typischerweise keine Funktion. Das ist der zweitwichtigste Grund, warum das nicht funktioniert
(3) Der wichtigste Grund ist aber: then() wird auf dem Promise aufgerufen, das import zurückgibt, und zwar sofort, nachdem der import-Request abgesetzt ist. In modul
befindest sich das Promise, das aus import zurückkam, nicht das Modul, und dieses Promise ist in dem Moment definitiv noch unerfüllt. Also selbst wenn in modul
das Modul stünde, wäre das Modul jetzt noch nicht verfügbar.
Dein zweiter Versuch kann eigentlich gar nicht klappen, weil Du an then() das Ergebnis von dr_konstruk2 übergeben willst. In dieser Funktion befindest Du Dich gerade, d.h. das ist eigentlich eine Endlos-Rekursion, die mit einem Stacküberlauf enden müsste.
Dein dritter Versuch verwendet await und versteckt damit das Promise vor Dir. Jetzt bekommst Du in p_dr_konstruk2 korrekt das Modul und solltest auch die vom Modul exportierte Funktion dr_konstruk2 aufrufen können. Wenn das Programm nicht endet, dann ist node der Meinung, dass da noch Arbeit offen ist. Das kann viele Gründe haben, dafür müsste ich jetzt selbst mit node und Promises Versuche machen. Was ich mangels verfügbarem Gerät in dieser Woche nicht kann. Ich weiß nicht, ob Node so lange weiterläuft, wie es Promises im Zustand "pending" gibt. Hast Du mal mit console.log im Programm Marker ausgegeben, wo er gerade ist?
Um den Ablauf nochmal zu verdeutlichen: import() schickt den Laderequest für das Modul ab und liefert ein Promise. Dieses wird erfüllt, sobald das Modul da ist. Wenn Du das mit .then machen willst, dann so:
import("./mymodule.js")
.then(function(mymodule) {
console.log("2. Import ist fertig");
mymodule.myFunction(x,y,z,a,b,c);
tuMehr();
});
console.log("1. Import ist bestellt");
tuwas();
Was hier wichtig zu wissen ist: Die Funktion, die .then übergeben wird, wird nicht in dem Moment aufgerufen, wo .then() läuft. Sie wird lediglich erstellt und an .then übergeben. Der Aufruf erfolgt im Hintergrund (als "Mikrotask"), wenn das Promise sich erfüllt. Die beiden console.log-Aufrufe im Beispiel sind nicht falschrum nummeriert, er loggt zuerst "bestellt", führt dann tuwas() aus und danach kommt erst "fertig". Heißt auch: der tuwas()-Aufruf kann noch nicht auf die Ergebnisse von myFunction() zugreifen. Weil die noch gar nicht gelaufen ist. Der tuMehr()-Aufruf hingegen findest nach dem Aufruf von myFunction statt und kann damit deren Ergebnisse nutzen.
Ein JavaScript-Programm läuft auch unter Node in einer Eventloop, d.h. das Hauptprogramm läuft durch und hinterlässt dabei Eventhandler oder Callbacks, die das Ergebnis von asynchronen Aktivitäten verarbeiten. Solange das Hauptprogramm nicht zu Ende ist, kann der then-Callback nicht laufen.
Mit await versteckst Du diesen Mechanismus. Was in einer async-Funktion hinter await folgt, wird von JavaScript automatisch in eine Callback-Funktion für then() verpackt. Deshalb ist await auch nur in einer async-Funktion erlaubt (und auf dem Top-Level von ECMAScript-Modulen). Eine async-Funktion endet beim ersten await und gibt ein Promise zurück, das durch den Rückgabewert der Funktion erfüllt wird. Der Rest der Funktion wird ausgeführt, wenn das Promise, das Du awaitest, erfüllt wird.
Das heißt aber auch dies: Wenn Du dieses Programm laufen lässt:
let x = 3;
import("./mymodule.js")
.then(function(mymodule) {
mymodule.myFunction(x);
});
x = 5;
dann wird myFunction mit dem Wert 5 aufgerufen. NICHT mit dem Wert 3. Die Callback-Funktion für .then schließt die Variable x in ihre Closure ein und bewahrt die Variable damit für den Moment auf, wo der Callback ausgeführt wird, aber die Closure enthält einen Verweis auf die Variable, nicht ihren eingefrorenen Wert. Deshalb sieht die Funktion den letzten Wert, den x hatte, und nicht den, der im Moment des then-Aufrufs vorlag.
Promises sind nicht ganz einfach. Um Dir genauere Tipps zu geben, kennen wir zu wenig von deiner Software.
Was auch noch zu beachten ist: was Du mit import() lädst, bleibt geladen. Du kannst also mittels import() dynamisch entscheiden, welche Module Du brauchst, aber du kannst kein Modul entladen, das Du nicht mehr brauchst. Ich meine aber auch, dass ein zweimaliger Import des gleichen Moduls kein Problem ist. Der zweite Import liefert Dir genauso ein Promise wie der erste, aber er lädt den Code nicht nochmal, d.h. das Promise erfüllt sich schneller. Der .then-Aufruf erfolgt aber ebenfalls asynchron.
Rolf
sumpsi - posui - obstruxi