Falls Ihr Node.js/JavaScript und Promises verwendet: Diesen Talk solltet Ihr gesehen haben. Schickt ihn am besten auch an Eure Kollegen. Prämisse: Ihr benutzt wahrscheinlich Promises falsch. Kann man auch auf 1.25-facher Geschwindigkeit schauen.
Posts mit Tag "nodejs"
Node.js hat eine API für einen asynchronen LocalStorage bekommen. Mal schauen, was es für coole Use-Cases es für den asyncLocalStorage
gibt. Erinnert ein bisschen an Reacts useContext()
.
Neu:
- Support für
Intl.DisplayNames
- Eine Promise-basierte
readline
-API:
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'process';
const rl = readline.createInterface({ input, output });
const answer = await rl.question('What do you think of Node.js? ');
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
Bei Node.js-Imports kann man jetzt node:
als Protokoll vor ein Paketnamen schreiben, um zu verdeutlichen, dass es ein Node-Builtin-Modul ist.
Node.js kann jetzt Deep-Copys machen. Funktioniert mit structuredClone
, was auch die Browser schon können.
Nie wieder JSON.parse(JSON.stringify(a))
und sich über kaputte Date
s ärgern.
Wo wir gerade bei Node.js waren. Wir hatten schon ein paar mal den AbortController
hier. Da gibt es jetzt eine Convenience-Method: AbortSignal.timeout(delay)
.
Node.js bekommt built-in Test-Runner. Aus dem Post:
const test = require('node:test');
const assert = require('assert');
test('synchronous passing test', (t) => {
// This test passes because it does not throw an exception.
assert.strictEqual(1, 1);
});
Wann kommt es?
Colin, the architect behind this initiative, has indicated that it will be available behind an experimental flag when Node 18 is pushed out, which is currently scheduled for April 19th.
It’s also available in nightly builds released after the 24th of March, 2022.
Falls es nicht nur an mir vorbei ging: Node.js 19.
Node.js bekommt ein Permission-System, ähnlich zu dem von Deno. Damit kann man Zugriff auf das Dateisystem oder Netzwerk einschränken.
Node 20.6 ist da und kann nativ .env-Files laden. Damit kann man in einfachen Fällen das dotenv-Package rauswerfen. Nicht nur das, denn die Env-File wird von Node selbst geladen, nicht von der Anwendung. Das heißt, damit kann man auch Env-Vars setzen, die das Verhalten von Node selbst beeinflussen (z. B. NODE_OPTIONS
).
Gibt keine wirklich spannenden Updates. Mein Highlight ist das hier: > A new experimental flag to flip module defaults (–experimental-default-type) – Node.js has two module systems: CommonJS modules and ECMAScript modules. Node.js treats files with a .js extension by default as CommonJS modules. This can now more easily be flipped.
Damit kann man jetzt immerhin ESM-Dateien ausführen, ohne eine package.json anzulegen.
Bun kann mittlerweile nicht nur Rust und sonst irgendwie alles importieren, sondern auch SQLite-Datenbanken:
import db from './my.db' with { type: "sqlite" };
const { id } = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
Man kann mit Bun seine Anwendung ja auch in eine einzige Binary bundlen. Wenn man das macht beim obigen, muss beim Ausführen die DB im aktuellen Verzeichnis liegen. Man kann die DB aber auch in dei Binary embedden:
import db from './my.db'
with {
type: "sqlite",
// Embed the database into the executable
embed: "true"
};
const { id } = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
Ob die DB da aber nur im Readonly-Modus geöffnet wird, wird im Blog nicht beschrieben. Ich gehe mla davon aus.
Deno hat gerade eine Version mit der Temporal API geshippt. Das war schon sehr lange in Stage 3, aber durch Abhängigkeit zu anderen Prozesse durften noch keine Implementierungen ohne Feature-Flag geshippt werden. Vor drei Wochen wurde der Hinweis dazu entfernt. Deno dürfte damit die erste Plattform sein, die es hat.
Hoffentlich ziehen die anderen nach, die API ist lange überfällig.
Edit:
Bei Deno ist es noch hinter einem Flag: --unstable-temporal
. Ich glaube, sie sind trotzdem die ersten, die es überhaupt shippen.
Node.js 22.4.0 ist da und hat die Web Storage API hinter einem Flag. Bei Node kann man also jetzt localStorage
und sessionStorage
benutzen.
Ich habe ein bisschen damit gespielt.
Der sessionStorage
ist für eine Programmlaufzeit gültig. Ist also nach einem Neustart weg. localStorage
geht auch über Neustarts hinweg. Dafür muss man aber eine explizite Datei angeben: node --localstorage-file foo
. Die Datei ist einfach eine SQLIte-Datenbank (wie die anderen Browser das intern auch machen):
$ echo 'localStorage.setItem("hallo", "welt")' | node --experimental-webstorage --localstorage-file foo
$ sqlite3 foo 'select * from nodejs_webstorage'
key value
----- -----
hallo welt
Ist also eine super Alternative für Key-Value-Storage, wenn man nicht extra SQLite installieren will.
Storage Events scheinen (noch?) nicht zu funktionieren. Da wär auch die Frage, ob die bei localStorage nur innerhalb des selben Prozesses funktionieren, oder ob alle Node-Instanzen da kommunizieren, wenn sie einen Wert in dieselbe Datenbank schreiben.
Node.js kann jetzt SQLite. Für die WebStorage-API neulich haben sie es ja schon eingebaut. Jetzt exposen sie auch die interne API nach außen.
import { DatabaseSync } from 'node:sqlite';
const database = new DatabaseSync(':memory:');
// Execute SQL statements from strings.
database.exec(`
CREATE TABLE data(
key INTEGER PRIMARY KEY,
value TEXT
) STRICT
`);
// Create a prepared statement to insert data into the database.
const insert = database.prepare('INSERT INTO data (key, value) VALUES (?, ?)');
// Execute the prepared statement with bound values.
insert.run(1, 'hello');
insert.run(2, 'world');
Sieht nur semi-kompatible mit dem SQLite von Bun aus.
Node.js kann bald TypeScript. Damit wäre der größte Vorteil von Bun in Node.
Ich hab jetzt mal Node.js selbst gebaut, um das Feature mit der TS-Ausführung zu testen und bin positiv überrascht vom Bauprozess. Es ist:
git clone https://github.com/nodejs/node
cd node
./configure
make -j24
Es funktioniert einfach. Neben GCC und GNU/Make braucht man noch ein supportetes Python. Sind jetzt keine krassen Anforderungen. Hat (bis jetzt) auch noch keine Magic gemacht und irgendwelche Sachen aus dem Internet geladen oder global installiert.
Matt Pocock hat frohe Kunde verbreitet und angekündigt, dass in der nächsten Node-Version das “Strip Types” nicht mehr hinter einem Flag ist. Das heißt, dass Node.js bald nativ TypeScript ausführen kann. Die Types werden einfach rausgenommen und alles wie JS interpretiert. D.h. kein TS-Compiler-Check und keine Runtime-Checks. Ersteres ist quasi nicht möglich, ohne TSC in Node.js zu integrieren. Das ist etwas doof, weil TypeScript keine Spec hat, sondern nur eine Referenzimplementierung. Runtime-Checks haben dasselbe Problen und manche Typen sind in TS so komplex, dass allein das Type-Checken bspw. eines Funktionsparameters mehr Zeit in Anspruch nehmen kann, als die eigentliche Funktion. Insoweit gut, dass beides kein Teil hiervon ist.
Was bei ihm aber nicht steht und was durchaus wichtig ist:
Imports müssen auf .ts
enden
Das geht gegen die bisherige Vorgehensweise von Node-Devs bei JS (die haben üblicherweise die Erweiterung weggelassen) und gegen die Empfehlung von TypeScript selbst (.js
verwenden, auch, wenn es eine .ts
-Datei ist). Die Empfehlung der TS-Leute ist aligned mit dem, was tatsächlich iim ESM-Standard steht. TypeScript hat bisher auch darauf verzichtet, Import-Pfade umzuschreiben.
Bun hat hierfür einen Resolution-Algorithmus, der bei einem Import von ./foo
von ./foo.js
über ./foo.ts
und ./foo.mts
etc alles absucht. Hier kann es zu unerwartetem Verhalten kommen, wenn eine ./foo.js
und eine ./foo.ts
existiert. Aber hier ist es immerhin möglich, seine Imports zu lassen, wie sie sind. tsx
(ein Node-Wrapper, der ähnlich wie die jetzige Implementierung von Node-Type-Stripping funktioniert) kann das auch.
Path-Aliase
Node möchte hier jetzt .ts
in Imports. TypeScript hat in den sauren Apfel gebissen und 2 Compiler-Flags dafür eingebaut: allowImportingTsExtensions
und rewriteRelativeImportExtensions
. Ersteres erlaubt .ts
in imports. Letzteres macht nach dem Kompilieren ein .js
aus .ts
. Das ist praktisch, um nach dem Kompilieren noch validen JS-Code zu haben, denn sonst würde dort in den Imports js .ts
stehen.
Was nicht umgeschrieben wird, sind nicht-relative imports. D. h. alle Imports, die nicht mit ./
oder ../
beginnen, bleiben so.
Manche Projekte haben einen "@/*"
-Path-Alias, der auf das Root-Verzeichnis des Projekts zeigt. Falls ihr das habt, könnt ihr also nicht auf Node.js mit TS-Direktausführung migrieren. Dafür braucht man dann noch irgendeinen extra-loader wie bisher auch. Oder ihr benutzt Bun oder tsx
. Damit das funktioniert, muss Node.js die tsconfig.json
lesen und verarbeiten. Ich denke aktuell, dass das zu komplex wär, dass das Node-Team das umsetzt.
Bisher wird es auch dabei bleiben.
Also insgesamt leider noch ein ferner Traum, dass man TS für bestehende Projekte einfach mit Node.js direkt ausführen kann. Dafür braucht man immer noch einen modifizierten Loader, bun, Deno, ts-node oder tsx.
Heute im Changelog von Bun gesehen: Node.js hat eine Histogram-API: createHistogram
:
import { createHistogram } from "perf_hooks";
// Create a histogram that can record values between 1 and 1,000,000,
// maintaining 3 significant figures of precision.
const histogram = createHistogram({
lowest: 1,
highest: 1_000_000,
figures: 3,
});
histogram.record(100);
histogram.record(200);
histogram.record(1000);
histogram.record(100); // Record a duplicate
console.log("Min:", histogram.min);
console.log("Max:", histogram.max);
console.log("Mean:", histogram.mean);
console.log("Standard Deviation:", histogram.stddev);
console.log("Total Count:", histogram.totalCount);
console.log("Percentile 50 (Median):", histogram.percentile(50));
Node.js hat ja schon SQLite eingebaut bekommen. In der neuen Node.js 24.9 gibt es dafür jetzt eine Erweiterung. Die API ist ähnlich zu der von Bun oder den tagged-template-strings, die ich mal als PoC statisch typisiert habe.
Aussehen tut das so:
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync(':memory:');
const sql = db.createSQLTagStore();
db.exec('CREATE TABLE users (id INT, name TEXT)');
// Using the 'run' method to insert data.
// The tagged literal is used to identify the prepared statement.
sql.run`INSERT INTO users VALUES (1, 'Alice')`;
sql.run`INSERT INTO users VALUES (2, 'Bob')`;
// Using the 'get' method to retrieve a single row.
const id = 1;
const user = sql.get`SELECT * FROM users WHERE id = ${id}`;
console.log(user); // { id: 1, name: 'Alice' }
// Using the 'all' method to retrieve all rows.
const allUsers = sql.all`SELECT * FROM users ORDER BY id`;
console.log(allUsers);
// [
// { id: 1, name: 'Alice' },
// { id: 2, name: 'Bob' }
// ]
Die Querys werden intern prepared und in einem LRU-Cache abgelegt. In den Docs steht leider nicht, wie man Arrays oder Sub-Querys übergibt. Das wird sicher bald bei den Typen nachgereicht.
Node.js bekommt ein rudimentäres Command-Line-Parsing. Damit wären für viele kleineren Tools keine librarys mehr notwendig.