48 lines
1.1 KiB
JavaScript
48 lines
1.1 KiB
JavaScript
/**
|
|
* Async lock utility - Promise-based mutex per key.
|
|
* Ensures only one async operation executes per key at a time.
|
|
*/
|
|
|
|
const locks = new Map()
|
|
|
|
/**
|
|
* Get or create a queue for a lock key.
|
|
* @param {string} lockKey - Lock key
|
|
* @returns {Promise<any>} Existing or new queue promise
|
|
*/
|
|
const getOrCreateQueue = (lockKey) => {
|
|
const existingQueue = locks.get(lockKey)
|
|
if (existingQueue) return existingQueue
|
|
const newQueue = Promise.resolve()
|
|
locks.set(lockKey, newQueue)
|
|
return newQueue
|
|
}
|
|
|
|
/**
|
|
* Acquire a lock for a key and execute callback.
|
|
* Only one callback per key executes at a time.
|
|
* @param {string} key - Lock key
|
|
* @param {Function} callback - Async function to execute
|
|
* @returns {Promise<any>} Result of callback
|
|
*/
|
|
export async function acquire(key, callback) {
|
|
const lockKey = String(key)
|
|
const queue = getOrCreateQueue(lockKey)
|
|
|
|
const next = queue.then(() => callback()).finally(() => {
|
|
if (locks.get(lockKey) === next) {
|
|
locks.delete(lockKey)
|
|
}
|
|
})
|
|
|
|
locks.set(lockKey, next)
|
|
return next
|
|
}
|
|
|
|
/**
|
|
* Clear all locks (for testing).
|
|
*/
|
|
export function clearLocks() {
|
|
locks.clear()
|
|
}
|