import type { KeyResolver } from "./singleFlight"; /** * 互斥锁:挂在同一个实例上的任务串行执行,前一个完全结束后下一个才开始。 * * 典型场景是「读-改-写」共享资源。读和写各是一次异步调用,中间可以被其他 * 调用插入,导致后写的覆盖先写的(lost update);把整段操作放进同一把锁里 * 就不会交错。 * * 与 {@link serialize} 的区别:`Mutex` 是**可共享的对象**,多个不同的函数 * 可以挂在同一把锁上。读-改-写同一份资源的一组函数(读取 / 写入 / 删除) * 必须共用一把锁,各自包一层 `serialize` 是挡不住的。 * * ⚠️ 锁内的任务不能再次调用同一把锁的 `run`,会排在自己后面造成死锁。 * * @example * ```ts * const storageLock = new Mutex(); * const take = (id: string) => storageLock.run(async () => { ... }); * const drop = (id: string) => storageLock.run(async () => { ... }); * // take 与 drop 并发时也不会交错 * ``` */ export declare class Mutex { /** * 队尾。始终保持为「永不 reject」的分支, * 否则某次任务失败且无人接手时会冒出 unhandled rejection。 */ private tail; private running; /** 排队中与执行中的任务总数。 */ get size(): number; /** 队列是否已排空。 */ get idle(): boolean; /** * 排队执行一个任务。 * * @param func - 需要串行执行的任务,同步函数也可以。 * @returns 任务的结果;任务抛出的异常会原样传给调用方。 */ run(func: () => T | Promise): Promise; } /** * 按 key 分组的互斥锁:相同 key 串行,不同 key 互不阻塞。 * * 队列排空后会自动回收对应的锁对象,key 不会无限增长。 * * 注意分组的语义:它保护的是「同一个 key 对应的资源」。如果多个 key 的 * 任务操作的其实是同一份共享资源,分组反而挡不住竞态,这种情况应当用 * 单个 {@link Mutex}。 */ export declare class KeyedMutex { private locks; /** 当前持有锁的 key 数量。 */ get size(): number; /** * 在指定 key 的队列上排队执行任务。 * * @param key - 并发槽位。 * @param func - 需要串行执行的任务。 * @returns 任务的结果;任务抛出的异常会原样传给调用方。 */ run(key: string, func: () => T | Promise): Promise; } /** * 把一个函数包成「调用自动排队」的版本,是 {@link KeyedMutex} 的语法糖。 * * 只需要串行化**单个函数**时用它;多个函数共享一份资源时请直接共用一个 * {@link Mutex} 实例。 * * ⚠️ 被包装的函数不能通过包装后的入口调用自己,会死锁。 * * @param func - 需要串行化的函数,同步函数也可以。 * @param options - 配置项,可通过 `key` 按入参分组。 * @returns 包装后的函数,调用会自动排队。 * * @example * ```ts * const writeProfile = serialize( * async (id: string, patch: object) => { ... }, * { key: (id) => id } * ); * ``` */ export declare function serialize(func: (...args: Args) => T | Promise, options?: { key?: KeyResolver; }): (...args: Args) => Promise;