Das Problem
JavaScript ist single-threaded, aber async/await erzeugt interleaving zwischen Tasks. Wenn mehrere asynchrone Operationen auf geteiltem Zustand arbeiten, braucht man einen Mutex.
Eine einfache Implementierung
type Release = () => void;
class AsyncMutex {
private queue: Array<(release: Release) => void> = [];
private locked = false;
acquire(): Promise<Release> {
return new Promise((resolve) => {
const tryAcquire = (release: Release) => {
if (!this.locked) {
this.locked = true;
resolve(release);
} else {
this.queue.push(tryAcquire);
}
};
const release: Release = () => {
this.locked = false;
const next = this.queue.shift();
if (next) next(release);
};
tryAcquire(release);
});
}
}
None-Typen mit Symbol
Statt null | undefined lässt sich ein Symbol-basierter None-Typ präziser typisieren:
const None = Symbol('None');
type None = typeof None;
type Option<T> = T | None;
function isNone<T>(value: Option<T>): value is None {
return value === None;
}
Fazit
Closure-basierte Mutexe sind in TypeScript idiomatisch und typsicher. Symbol-basierte None-Typen vermeiden die Ambiguität zwischen null, undefined und legitimem Fehlerfehlen eines Wertes.