Skip to content

queueByKey

Queues async function calls by key to ensure sequential execution per key

215 bytes
since v12.6.0

Usage

Wraps an asynchronous function to ensure that calls with the same key are queued and executed sequentially, while calls with different keys can run in parallel. This is useful for preventing race conditions when operations must not overlap for the same logical group (like user ID or resource ID).

import * as
import _
_
from 'radashi'
const
const updateUser: (userId: string, data: object) => Promise<any>
updateUser
= async (
userId: string
userId
: string,
data: object
data
: object) => {
// Simulate API call that shouldn't overlap for the same user
const
const response: Response
response
= await
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)
fetch
(`/api/users/${
userId: string
userId
}`, {
RequestInit.method?: string

A string to set request's method.

method
: 'POST',
RequestInit.body?: BodyInit | null

A BodyInit object or null to set request's body.

body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
(
data: object
data
),
})
return
const response: Response
response
.
Body.json(): Promise<any>
json
()
}
const
const queuedUpdate: (userId: string, data: object) => Promise<any>
queuedUpdate
=
import _
_
.
function queueByKey<[userId: string, data: object], any>(asyncFn: (userId: string, data: object) => any, keyFn: (userId: string, data: object) => string | number): (userId: string, data: object) => Promise<any>

Queues async function calls by key to ensure sequential execution per key. Calls with the same key are executed sequentially, while calls with different keys can run in parallel.

@seehttps://radashi.js.org/reference/async/queueByKey

@example

const updateUser = async (userId: string, data: object) => {
// API call that should not overlap for the same user
return fetch(`/api/users/${userId}`, { method: 'POST', body: JSON.stringify(data) })
}
const queuedUpdate = queueByKey(updateUser, (userId) => userId)
// These will run sequentially for user123
queuedUpdate('user123', { name: 'Alice' })
queuedUpdate('user123', { age: 30 })
// This runs in parallel with user123's queue
queuedUpdate('user456', { name: 'Bob' })

@version12.6.0

queueByKey
(
const updateUser: (userId: string, data: object) => Promise<any>
updateUser
,
userId: string
userId
=>
userId: string
userId
)
// These will run sequentially for user123
const queuedUpdate: (userId: string, data: object) => Promise<any>
queuedUpdate
('user123', {
name: string
name
: 'Alice' })
const queuedUpdate: (userId: string, data: object) => Promise<any>
queuedUpdate
('user123', {
age: number
age
: 30 })
// This runs in parallel with user123's queue
const queuedUpdate: (userId: string, data: object) => Promise<any>
queuedUpdate
('user456', {
name: string
name
: 'Bob' })

Key Features

  • Sequential per key: Operations with the same key execute one after another
  • Parallel across keys: Operations with different keys run concurrently
  • Error handling: Errors are properly propagated and don’t break the queue
  • Memory efficient: Queues are automatically cleaned up when empty
  • Type safe: Full TypeScript support with generic types

Advanced Example

import * as
import _
_
from 'radashi'
// Database operations that must be serialized per table
const
const dbOperation: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
dbOperation
= async (
table: string
table
: string,
operation: string
operation
: string,
data: any
data
: any) => {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(`Starting ${
operation: string
operation
} on ${
table: string
table
}`)
await new
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>

Creates a new Promise.

@paramexecutor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.

Promise
(
resolve: (value: unknown) => void
resolve
=>
function setTimeout(callback: (_: void) => void, delay?: number): NodeJS.Timeout (+2 overloads)
setTimeout
(
resolve: (value: unknown) => void
resolve
, 100)) // Simulate work
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(`Completed ${
operation: string
operation
} on ${
table: string
table
}`)
return {
table: string
table
,
operation: string
operation
,
data: any
data
}
}
// Queue by table name to prevent concurrent operations on same table
const
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
=
import _
_
.
function queueByKey<[table: string, operation: string, data: any], {
table: string;
operation: string;
data: any;
}>(asyncFn: (table: string, operation: string, data: any) => {
table: string;
operation: string;
data: any;
} | PromiseLike<{
table: string;
operation: string;
data: any;
}>, keyFn: (table: string, operation: string, data: any) => string | number): (table: string, operation: string, data: any) => Promise<...>

Queues async function calls by key to ensure sequential execution per key. Calls with the same key are executed sequentially, while calls with different keys can run in parallel.

@seehttps://radashi.js.org/reference/async/queueByKey

@example

const updateUser = async (userId: string, data: object) => {
// API call that should not overlap for the same user
return fetch(`/api/users/${userId}`, { method: 'POST', body: JSON.stringify(data) })
}
const queuedUpdate = queueByKey(updateUser, (userId) => userId)
// These will run sequentially for user123
queuedUpdate('user123', { name: 'Alice' })
queuedUpdate('user123', { age: 30 })
// This runs in parallel with user123's queue
queuedUpdate('user456', { name: 'Bob' })

@version12.6.0

queueByKey
(
const dbOperation: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
dbOperation
,
table: string
table
=>
table: string
table
, // Key function extracts table name
)
// These operations on 'users' table will run sequentially
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
('users', 'insert', {
name: string
name
: 'Alice' })
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
('users', 'update', {
id: number
id
: 1,
name: string
name
: 'Bob' })
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
('users', 'delete', {
id: number
id
: 2 })
// These operations on 'orders' table run in parallel with 'users' operations
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
('orders', 'insert', {
userId: number
userId
: 1,
total: number
total
: 100 })
const queuedDbOp: (table: string, operation: string, data: any) => Promise<{
table: string;
operation: string;
data: any;
}>
queuedDbOp
('orders', 'update', {
id: number
id
: 1,
status: string
status
: 'shipped' })

Error Handling

Errors from the wrapped function are properly propagated to the caller, and the queue continues processing subsequent calls:

import * as
import _
_
from 'radashi'
const
const riskyOperation: (id: string, shouldFail: boolean) => Promise<string>
riskyOperation
= async (
id: string
id
: string,
shouldFail: boolean
shouldFail
: boolean) => {
if (
shouldFail: boolean
shouldFail
) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
(`Operation failed for ${
id: string
id
}`)
}
return `Success for ${
id: string
id
}`
}
const
const queuedRiskyOp: (id: string, shouldFail: boolean) => Promise<string>
queuedRiskyOp
=
import _
_
.
function queueByKey<[id: string, shouldFail: boolean], string>(asyncFn: (id: string, shouldFail: boolean) => string | PromiseLike<string>, keyFn: (id: string, shouldFail: boolean) => string | number): (id: string, shouldFail: boolean) => Promise<...>

Queues async function calls by key to ensure sequential execution per key. Calls with the same key are executed sequentially, while calls with different keys can run in parallel.

@seehttps://radashi.js.org/reference/async/queueByKey

@example

const updateUser = async (userId: string, data: object) => {
// API call that should not overlap for the same user
return fetch(`/api/users/${userId}`, { method: 'POST', body: JSON.stringify(data) })
}
const queuedUpdate = queueByKey(updateUser, (userId) => userId)
// These will run sequentially for user123
queuedUpdate('user123', { name: 'Alice' })
queuedUpdate('user123', { age: 30 })
// This runs in parallel with user123's queue
queuedUpdate('user456', { name: 'Bob' })

@version12.6.0

queueByKey
(
const riskyOperation: (id: string, shouldFail: boolean) => Promise<string>
riskyOperation
,
id: string
id
=>
id: string
id
)
try {
await
const queuedRiskyOp: (id: string, shouldFail: boolean) => Promise<string>
queuedRiskyOp
('user1', true) // This will throw
} catch (
var error: unknown
error
) {
if (
var error: unknown
error
instanceof
var Error: ErrorConstructor
Error
) {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
var error: Error
error
.
Error.message: string
message
) // "Operation failed for user1"
}
}
// Queue continues normally
const
const result: string
result
= await
const queuedRiskyOp: (id: string, shouldFail: boolean) => Promise<string>
queuedRiskyOp
('user1', false)
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const result: string
result
) // "Success for user1"