# loqkit 中文文档

规范地址：https://npmcn.com/package/loqkit/

版本：0\.8\.4
npm 元数据更新时间：2026-11-04T00:35:13.863Z
中文译文更新时间：2026-11-04T02:32:34.777Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/loqkit
原文对照：https://npmcn.com/package/loqkit/?lang=original
项目主页：https://github.com/regbo/lfp-js/tree/main/packages/loqkit#readme
源码仓库：https://github.com/regbo/lfp-js

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：588 kB，71 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：5 个
- 首次发布：2026-11-01T13:57:12.507Z
- 最新版本发布：2026-11-04T00:35:13.499Z
- 已发布版本数：13（近 12 个月 13 个）

## 中文简介

JavaScript运行时的类型化记忆和分层内存、工作线程和文件锁。

## 安装

```sh
npm install loqkit
```

也可以使用：`pnpm add loqkit`、`yarn add loqkit`、`bun add loqkit`

## README

# loqkit

JavaScript运行时的类型记忆和分层锁定。

Loqkit发布匹配的ESM和CommonJS入口点。

## 锁定

```ts
import { withLock } from "loqkit";

const result = await withLock(
  { resource: "cache" },
  async ({ resource, lock }) => {
    console.log(`locked ${resource} with ${lock.backends.length} layers`);
  },
  {
    key: "cache-refresh",
    predicate: async ({ resource, state }) => {
      return state === "predicate-after"
        ? await stillNeedsRefresh(resource)
        : true;
    },
    onCheckFailed: async ({ resource, state }) => {
      console.log(`${resource} skipped in ${state}`);
    },
  },
);

console.log(result.value, result.context.state, result.context.lock.waitedMs);
```

默认情况下, `withLock` 需要一个内存锁，使用工作线程和本机 `flock` 在可用时锁定，并在跳过本机群集时使用可移植轮询。动作后的参数可以是谓词函数或options对象，其中包含 `predicate`． 谓词在等待之前运行一次，在处理每个启用的层之后再运行一次。只有当两个检查都通过时，操作才会运行。

每个回调都会收到 `ctx.state`： `"predicate-before"` 对于第一张支票， `"predicate-after"` 用于收购后的支票，以及 `"executed"` 为了行动。当其中一个检查失败时， `onCheckFailed(ctx)` 在所有获得的层被释放后运行。它可以检查 `ctx.state`、记录、重读数据或启动其他锁工作而不持有此锁。

`withLock` 返回 `{ value, context }`。 `value` 是行动的结果，还是 `undefined` 操作未运行时。返回的上下文具有与回调上下文相同的字段和状态值 `"timeout"` 添加，因为非抛出超时只能在结果中观察到。锁元数据（包括等待时间）在 `result.context.lock`。

省略 `timeout` 无限期地等待。一个数值超时抛出 `LockTimeoutError` 当它到期时。使用对象形式返回超时结果。零超时只进行一次非阻塞的获取尝试，并且不会休眠或轮询第二次：

```ts
const attempt = await withLock(context, action, {
  timeout: { ms: 0, error: false },
});

if (attempt.context.state === "timeout") {
  console.log("Lock is busy");
}
```

`scope` 是部分配置映射。省略映射或属性使用其默认值； `false` 禁用该层。工人和羊群另外接受 `"auto"`，它在可用时使用该层，否则将其记录为跳过：

```ts
await withLock(context, action, {
  scope: { worker: false, flock: false },
});

await withLock(context, action, {
  scope: { memory: false, worker: true, flock: false, poll: false },
});
```

| 范围 | 值 | 默认的 | 行为 |
| --- | --- | --- | --- |
| `memory` | `true`， `false` | `true` | 当前JavaScript隔离中的锁。 |
| `worker` | `true`， `false`， `"auto"` | `"auto"` | 协调工作线程。 `true` 抛出 `LockUnavailableError` 当运行时API不可用时。 |
| `flock` | `true`， `false`， `"auto"` | `"auto"` | 使用本机咨询文件锁定。 `true` 抛出 `LockUnavailableError` 本机支持不可用或失败时。 |
| `poll` | `true`， `false`， `"auto"` | `"auto"` | 使用可移植轮询文件锁。 `"auto"` 当获得本地羊群或其他需要时跳过它。 |

启用的层总是运行 `memory`， `worker`， `flock`， `poll` 顺序和释放顺序相反。将所有四个设置为 `false` 允许的，并且运行谓词和操作时没有独占性边界。

上的回调和结果公开已解析的结果 `ctx.lock.scope`． 它包含一个布尔值for `memory`, `true`， `false`,或 `"skipped"` 为 `worker`， `flock`, `poll`； `"auto"` 从来没有出现过。自动层可以从 `true` 在第一个谓词上下文中to `"skipped"` 收购后确定它不受支持或冗余。 `ctx.lock.backends` 保留低级实现和回退细节。

## 记忆有关

`memoize` 将零参数工厂转换为计算一次并重用其成功结果的getter。同步工厂保持同步。异步或可启用的工厂返回原生承诺，并发调用者共享相同的动态工作。

```ts
import { memoize } from "loqkit";

const getConfig = memoize(() => ({ region: "us-east-1" }));
const config = getConfig();

const getCatalog = memoize(async () => {
  const response = await fetch("https://example.com/catalog.json");
  return response.json();
});

const catalog = await getCatalog();
```

成功的结果将在记忆getter的生命周期内保持缓存。抛出的错误和拒绝的承诺不会被缓存，因此下一个调用将重试。没有TTL或过期行为。

```mermaid
flowchart TD
    call["Call memoized getter"] --> cached{"Successful result cached?"}
    cached -- "Yes" --> reuse["Return cached value or promise"]
    cached -- "No" --> factory["Run factory"]
    factory --> kind{"Thenable result?"}
    kind -- "No" --> storeValue["Cache and return value"]
    kind -- "Yes" --> storePromise["Cache and return in-flight promise"]
    storePromise --> outcome{"Promise outcome"}
    outcome -- "Resolved" --> keep["Keep successful promise"]
    outcome -- "Rejected" --> evict["Evict so the next call retries"]
```

## 锁是如何工作的

`withLock` 是一个键作用域锁。具有相同名称空间和键的调用一次只运行一个；具有不同键的调用可以并发运行。如果没有提供键，loqkit将上下文散列到一个稳定的键中。

当 `scope` 省略时，loqkit按顺序处理这些层，并反向释放获得的层：

1.  一个内存中的 [`async-mutex`](https://github.com/DirtyHairy/async-mutex) lock序列化当前JavaScript隔离中的调用者。
2.  Node的工作锁管理器在运行时提供时协调工作线程。默认的 `"auto"` mode在API不可用时将该层记录为跳过。
3.  在macOS和Linux上，可选的 [`fs-ext`](https://github.com/baudehlo/node-fs-ext) 图层使用native `flock`． 默认的 `"auto"` 当本机锁定不可用或获取失败（包括拒绝的文件系统）时，Mode将该层记录为跳过 `flock`。
4.  一个 [`proper-lockfile`](https://github.com/moxystudio/node-proper-lockfile) 轮询锁协调共享配置的锁目录的进程。它的默认 `"auto"` Mode在flock被跳过或禁用时获取它，在flock成功后跳过它。

只有在不可用或非争用获取失败时才绕过自动群层。普通的争用继续等待羊群，这样排他性得以保留。集 `scope.poll` 来 `true` 需要两种机制；由于flock已经序列化了loqkit调用者，因此在该状态下的轮询争用立即失败 `LockUnavailableError` 而不是永远等待。集 `scope.flock` 来 `false` 对于已知具有不可靠群语义的网络挂载。 `lockDirectory` 或 `LOQKIT_LOCK_DIR` 两个基于文件的层存储其锁文件的控件。

```mermaid
flowchart TD
    call["Call withLock"] --> options["Validate options and choose a key"]
    options --> checking["Create predicate-before context"]
    checking --> before{"Predicate passes before waiting?"}
    before -- "No" --> failedBefore["Run onCheckFailed without a lock"]
    failedBefore --> skippedBefore["Return predicate-before context"]
    before -- "Yes or absent" --> memory["Acquire memory lock"]
    memory --> worker["Acquire worker-thread lock"]
    worker --> flock["Acquire or bypass native flock"]
    flock --> poll["Acquire or bypass portable polling lock"]
    memory -. "Timeout" .-> timeout{"timeout.error?"}
    worker -. "Timeout" .-> timeout
    flock -. "Timeout" .-> timeout
    poll -. "Timeout" .-> timeout
    timeout -- "Yes" --> timeoutError["Throw LockTimeoutError"]
    timeout -- "No" --> timeoutResult["Return timeout context"]
    poll --> acquired["Create predicate-after context with enabled layers handled"]
    acquired --> after{"Predicate still passes?"}
    after -- "No" --> releaseFailed["Release all layers"]
    releaseFailed --> failedAfter["Run onCheckFailed without a lock"]
    failedAfter --> skippedAfter["Return predicate-after context"]
    after -- "Yes or absent" --> action["Run action with executed context"]
    action --> release["Release poll, flock, worker, then memory"]
    release --> result["Return value and executed context"]
```

当另一个调用者在此调用者等待时更改底层状态时，第二个谓词检查很重要：

```mermaid
sequenceDiagram
    participant A as Caller A
    participant B as Caller B
    participant L as Layered lock for one key

    A->>L: Acquire enabled memory, worker, flock, poll layers
    L-->>A: Granted
    B->>B: Check predicate
    B->>L: Acquire same layers
    Note over B,L: Caller B waits
    A->>A: Run action
    A-->>L: Release all layers
    L-->>B: Granted
    B->>B: Check predicate again
    B->>B: Run action if still needed
    B-->>L: Release all layers
```

默认的文件目录在主机本地。容器只有在共享配置的锁目录时才会协调，而单独的主机仍然需要共享锁服务或数据库支持的锁。

## 释放

运行 `bun run release` 从干净，同步 `main` 分支。Patch是默认增量； `minor`， `major`, `--dry-run` 也支持。现有的未标记的npm `0.0.1` 需要一次性引导表单 `bun run release -- --initial`。

本地命令验证Git标识，从远程GitHub标记派生下一个版本，运行完整的套件，并在自动推送发布提交和标记之前打包包。 [标签工作流](https://npmcn.com/package/loqkit/.github/workflows/publish.yml) 重复验证，因为发布标签不受保护，然后通过npm Trusted Publishing发布生成的存档，并注明出处。它不使用npm令牌。

## 常见问题

### loqkit 是什么？

JavaScript运行时的类型化记忆和分层内存、工作线程和文件锁。

### 如何安装 loqkit？

在项目目录运行 npm install loqkit，或使用 pnpm add / yarn add / bun add。

### loqkit 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### loqkit 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### loqkit 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### loqkit 可以商用吗？

loqkit 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### loqkit 还在维护吗？

最新版本 0\.8\.4 发布于 2026/11/04（1 天前）；至今共发布 13 个版本，其中过去 12 个月发布了 13 个；首次发布于 2026/11/01。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

