A mutual exclusion lock that ensures only one task accesses a resource at a time.
Constructor
const mutex = new Mutex();
Methods
acquire
Acquires the lock. Returns a release function that must be called to unlock.
acquire(signal?: AbortSignal): Promise<() => void>
Parameters
Optional AbortSignal to cancel waiting for the lock.
Returns
A promise that resolves with a release function. Call the release function to unlock the mutex.
Throws
AbortError if the signal is aborted while waiting
runExclusive
Acquires the lock, runs the function exclusively, and automatically releases the lock.
runExclusive<T>(fn: () => Promise<T>, signal?: AbortSignal): Promise<T>
Parameters
The function to run exclusively.
Optional AbortSignal to cancel waiting for the lock.
Returns
A promise that resolves with the result of the function.
Examples
Basic usage with acquire/release
import { Mutex } from '@temelj/async';
const mutex = new Mutex();
let sharedResource = 0;
async function incrementSafely() {
const release = await mutex.acquire();
try {
// Critical section
const current = sharedResource;
await delay(10);
sharedResource = current + 1;
} finally {
release();
}
}
// Safe concurrent execution
await Promise.all([
incrementSafely(),
incrementSafely(),
incrementSafely(),
]);
console.log(sharedResource); // 3
Using runExclusive
import { Mutex } from '@temelj/async';
const mutex = new Mutex();
let balance = 100;
async function withdraw(amount: number) {
return await mutex.runExclusive(async () => {
if (balance >= amount) {
await delay(10); // Simulate async operation
balance -= amount;
return true;
}
return false;
});
}
// Prevents race conditions
const results = await Promise.all([
withdraw(60),
withdraw(60),
]);
console.log(results); // [true, false]
console.log(balance); // 40
Database connection pool
import { Mutex } from '@temelj/async';
class Database {
private mutex = new Mutex();
private connection: Connection | null = null;
async query(sql: string) {
return await this.mutex.runExclusive(async () => {
if (!this.connection) {
this.connection = await createConnection();
}
return await this.connection.execute(sql);
});
}
}
With abort signal
import { Mutex } from '@temelj/async';
const mutex = new Mutex();
const controller = new AbortController();
try {
const result = await mutex.runExclusive(
async () => {
return await longRunningTask();
},
controller.signal
);
} catch (error) {
if (error instanceof AbortError) {
console.log('Lock acquisition cancelled');
}
}
// Cancel if taking too long
setTimeout(() => controller.abort(), 5000);
File writing coordination
import { Mutex } from '@temelj/async';
import { writeFile } from 'fs/promises';
const fileMutex = new Mutex();
async function appendToLog(message: string) {
await fileMutex.runExclusive(async () => {
const existing = await readFile('log.txt', 'utf-8');
await writeFile('log.txt', existing + message + '\n');
});
}
// Safe concurrent logging
await Promise.all([
appendToLog('Event 1'),
appendToLog('Event 2'),
appendToLog('Event 3'),
]);