Es gibt einen langlaufenden Witz in der Erlang-Community: "Warum lachen Erlang-Entwickler über andere Sprachen? Weil ihre Prozesse Supervisoren haben, die auf sie aufpassen." Das Fehlertoleranz-Modell der BEAM-VM — wo abgestürzte Prozesse automatisch von Supervisoren neugestartet werden, wo Fehler in einem Prozess andere nicht zum Absturz bringen, wo Hot-Code-Reloading in den Runtime eingebaut ist — ist genuin anders als das, woran die meisten Entwickler gewöhnt sind.
QuickBEAM (auf github.com/elixir-volt/quickbeam) bringt JavaScript in dieses Modell. Es führt JavaScript-Runtimes als BEAM-GenServer-Prozesse aus, integriert sie in OTP-Supervision-Trees und ermöglicht JavaScript-Code, direkt Elixir-Funktionen und OTP-Bibliotheken aufzurufen.
Was QuickBEAM tatsächlich ist
Im Kern ist QuickBEAM ein BEAM-GenServer, der einen JavaScript-Runtime umschließt. Jeder JS-Runtime ist ein Prozess im BEAM-VM-Scheduler, mit allen Garantien, die das mit sich bringt. Man startet einen Runtime:
{:ok, rt} = QuickBEAM.start()
{:ok, 3} = QuickBEAM.eval(rt, "1 + 2")
{:ok, "HELLO"} = QuickBEAM.eval(rt, "'hello'.toUpperCase()")
Zustand bleibt über Aufrufe innerhalb desselben Runtime bestehen, und man kann benannte JavaScript-Funktionen aufrufen:
QuickBEAM.eval(rt, "function greet(name) { return 'hi ' + name }")
{:ok, "hi world"} = QuickBEAM.call(rt, "greet", ["world"])
JavaScript trifft OTP
Der interessante Teil ist die Brücke zwischen JavaScript und dem umgebenden BEAM-Ökosystem. Man registriert Elixir-Handler beim Start des Runtime:
{:ok, rt} = QuickBEAM.start(handlers: %{
"db.query" => fn [sql] -> MyRepo.query!(sql).rows end,
"cache.get" => fn [key] -> Cachex.get!(:app, key) end,
})
{:ok, rows} = QuickBEAM.eval(rt, """
const rows = await Beam.call("db.query", "SELECT * FROM users LIMIT 5");
rows.map(r => r.name);
""")
JavaScript kann auch Nachrichten an beliebige BEAM-Prozesse senden, Prozesse auf Beendigung überwachen und bidirektional verknüpft werden — sodass ein JavaScript-Absturz genau wie jeder andere OTP-Prozess einen Supervisor-Neustart auslösen kann.
Eingebautes TypeScript-Toolkit
QuickBEAM wird mit einem eingebauten TypeScript-Toolkit ausgeliefert, was bemerkenswert ist, weil es TypeScript zu einem erstklassigen Bürger im BEAM-Ökosystem macht, anstatt es nachzurüsten. Das Projekt zielt auf Zig 0.15+ ab und verteilt über Hex:
def deps do
[{:quickbeam, "~> 0.7.1"}]
end
Supervision-Trees für JavaScript
Der praktische Vorteil davon ist Supervision-Trees, die JavaScript-Komponenten enthalten. Wenn ein JavaScript-Runtime abstürzt, übernimmt OTP's Supervision-Strategie die Wiederherstellung — Prozess neustarten, möglicherweise auf einem anderen BEAM-Knoten in einem verteilten Setup. Für Anwendungen, die BEAM-Zuverlässigkeit brauchen, aber in JavaScript geschriebene Komponenten haben, ist das eine direkte Antwort.
Man kann auch QuickBEAM.ContextPool verwenden, um einen Pool von JS-Runtime-Kontexten für hochkonkurrierende Szenarien zu erstellen — mehrere Runtime, die über Anfragen hinweg geteilt werden können.
Die Nische
QuickBEAM versucht nicht, Node.js oder Deno zu ersetzen. Es ist ein spezialisierter Runtime für Elixir/Erlang-Teams, die bestimmte Komponenten in JavaScript schreiben wollen, ohne das BEAM-Zuverlässigkeitsmodell aufzugeben. Die Integrationsfläche — Beam.call(), Prozess-Messaging, Supervision — ist der Punkt, nicht der Runtime selbst.
Für das breitere JavaScript-Ökosystem ist es ein Existenzbeweis, dass BEAMs Nebenläufigkeitsmodell von JavaScript aus zugänglich ist. Ob es eine echte Nutzerbasis findet, hängt davon ab, ob Teams, die hochzuverlässige Systeme in Elixir bauen, genügend Wert darin sehen, bestimmte Komponenten in JavaScript statt in Elixir selbst zu schreiben.
Das Projekt ist in Version 0.7.1 und aktiv in Entwicklung. Wenn Sie Elixir in Produktion betreiben und JavaScript-Code haben, dem Sie mehr vertrauen möchten, lohnt sich ein Blick.