queueByKey()
queueByKey<
T>(key,fn):Promise<T>
Queues async functions by key to ensure sequential execution.
๐ Why is this a Hidden Gem?
Ensures sequential execution by key. Prevents race conditions by forcing operations to run one after another.
Functions with the same key execute sequentially. Different keys run in parallel.
Type Parametersโ
T: Tโ
The return type of the function.
Parametersโ
key: stringโ
Unique key for the operation queue.
fn: () => Promise<T>โ
The async function to queue.
Returns: Promise<T>โ
Promise that resolves to the function result.
Throwsโ
Rejects if the queued function rejects.
See Alsoโ
dedupeByKey โ for deduplication instead of queuing.
Sinceโ
1.1.0
Performanceโ
Silently catches errors to prevent queue breakage; errors still propagate to caller.
Also known asโ
Queue (Modern Dash) ยท queueByKey (Radashi) ยท โ (Lodash, es-toolkit, Remeda, Ramda, Effect, Antfu)
Exampleโ
// Sequential execution for same key
queueByKey('user-123', () => updateUser('123'));
queueByKey('user-123', () => updateUser('123'));
// Second waits for first to complete
// Parallel execution for different keys
queueByKey('user-A', () => updateUser('A'));
queueByKey('user-B', () => updateUser('B'));
How it works?โ
Ensures sequential execution for functions sharing the same key. Different keys run in parallel โ same key waits for previous to complete.
Queue vs Dedupeโ
| queueByKey | dedupeByKey | |
|---|---|---|
| Same key calls | Sequential | Shared |
| Result | Each gets own | Same promise |
| Use case | Ordered writes | Duplicate reads |
Use Casesโ
Sequential Database Writes ๐โ
Ensure that updates to the same record happen strictly in order, preventing "Lost Update" anomalies. Essential for counters, inventory management, or financial balances.
// Safe inventory update
const decrementStock = async (productId: string) => {
return await queueByKey(`stock-${productId}`, async () => {
const current = await db.getStock(productId);
if (current > 0) {
await db.updateStock(productId, current - 1);
}
});
};
// Even if called in parallel, updates will run one after another
await Promise.all([
decrementStock("item-1"),
decrementStock("item-1"),
decrementStock("item-1")
]);
// Stock decreases by 3 correctly
Prevent File Corruptionโ
When appending to a file or updating a shared local resource, ensure only one write happens at a time.
// Safe log appending
const writeLog = async (message: string) => {
await queueByKey('system-log', async () => {
const timestamp = new Date().toISOString();
await fs.appendFile('system.log', `[${timestamp}] ${message}\n`);
});
};
Ordered message processingโ
Process messages for the same conversation in strict order. Essential for chat apps, event sourcing, or command queues.
const processMessage = async (conversationId: string, message: Message) => {
return await queueByKey(`conv-${conversationId}`, async () => {
// Messages for same conversation processed in order
await db.insertMessage(message);
await updateConversationTimestamp(conversationId);
await notifyParticipants(conversationId, message);
});
};
// Even if messages arrive out of order from WebSocket
socket.on("message", (msg) => {
processMessage(msg.conversationId, msg);
});
// Each conversation's messages are processed sequentially