queueByKey
按键对异步函数调用进行排队,确保每个键的顺序执行
215 bytes
since v12.6.0
使用方法
包装异步函数以确保具有相同键的调用排队并顺序执行,而具有不同键的调用可以并行运行。这对于防止同一逻辑组(如用户 ID 或资源 ID)的操作不重叠时的竞争条件很有用。
import * as _ from "radashi";
const updateUser = async (userId: string, data: object) => { // 模拟不应为同一用户重叠的API调用 const response = await fetch(`/api/users/${userId}`, { method: "POST", body: JSON.stringify(data), }); return response.json();};
const queuedUpdate = _.queueByKey(updateUser, (userId) => userId);
// 这些将为user123顺序运行queuedUpdate("user123", { name: "Alice" });queuedUpdate("user123", { age: 30 });
// 这与user123的队列并行运行queuedUpdate("user456", { name: "Bob" });主要特性
- 每个键顺序: 具有相同键的操作依次执行
- 跨键并行: 具有不同键的操作并发运行
- 错误处理: 错误被正确传播且不会破坏队列
- 内存高效: 队列在空时自动清理
- 类型安全: 完全支持 TypeScript 的泛型类型
高级示例
import * as _ from "radashi";
// 必须按表序列化的数据库操作const dbOperation = async (table: string, operation: string, data: any) => { console.log(`Starting ${operation} on ${table}`); await new Promise((resolve) => setTimeout(resolve, 100)); // 模拟工作 console.log(`Completed ${operation} on ${table}`); return { table, operation, data };};
// 按表名排队以防止同一表上的并发操作const queuedDbOp = _.queueByKey( dbOperation, (table) => table // 键函数提取表名);
// 这些在'users'表上的操作将顺序运行queuedDbOp("users", "insert", { name: "Alice" });queuedDbOp("users", "update", { id: 1, name: "Bob" });queuedDbOp("users", "delete", { id: 2 });
// 这些在'orders'表上的操作与'users'操作并行运行queuedDbOp("orders", "insert", { userId: 1, total: 100 });queuedDbOp("orders", "update", { id: 1, status: "shipped" });错误处理
包装函数的错误被正确传播给调用者,队列继续处理后续调用:
import * as _ from "radashi";
const riskyOperation = async (id: string, shouldFail: boolean) => { if (shouldFail) { throw new Error(`Operation failed for ${id}`); } return `Success for ${id}`;};
const queuedRiskyOp = _.queueByKey(riskyOperation, (id) => id);
try { await queuedRiskyOp("user1", true); // 这将抛出错误} catch (error) { console.log(error.message); // "Operation failed for user1"}
// 队列正常继续const result = await queuedRiskyOp("user1", false);console.log(result); // "Success for user1"