sequential.ts
utils/sequential.ts
No strong subsystem tag
57
Lines
1641
Bytes
1
Exports
0
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with general runtime concerns. It contains 57 lines, 0 detected imports, and 1 detected exports.
Important relationships
Detected exports
sequential
Keywords
argsqueueunknownresolvevoidrejectpromiseprocessingcontextsequential
Detected imports
- No import paths detected.
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
type QueueItem<T extends unknown[], R> = {
args: T
resolve: (value: R) => void
reject: (reason?: unknown) => void
context: unknown
}
/**
* Creates a sequential execution wrapper for async functions to prevent race conditions.
* Ensures that concurrent calls to the wrapped function are executed one at a time
* in the order they were received, while preserving the correct return values.
*
* This is useful for operations that must be performed sequentially, such as
* file writes or database updates that could cause conflicts if executed concurrently.
*
* @param fn - The async function to wrap with sequential execution
* @returns A wrapped version of the function that executes calls sequentially
*/
export function sequential<T extends unknown[], R>(
fn: (...args: T) => Promise<R>,
): (...args: T) => Promise<R> {
const queue: QueueItem<T, R>[] = []
let processing = false
async function processQueue(): Promise<void> {
if (processing) return
if (queue.length === 0) return
processing = true
while (queue.length > 0) {
const { args, resolve, reject, context } = queue.shift()!
try {
const result = await fn.apply(context, args)
resolve(result)
} catch (error) {
reject(error)
}
}
processing = false
// Check if new items were added while we were processing
if (queue.length > 0) {
void processQueue()
}
}
return function (this: unknown, ...args: T): Promise<R> {
return new Promise((resolve, reject) => {
queue.push({ args, resolve, reject, context: this })
void processQueue()
})
}
}