返回

文章详情

我为 TypeScript 制作了一个承诺感知的防抖和节流库

Hacker News2026年8月2日 11:07

现代 TypeScript 的小型、强类型计时和并发工具。每个计划的调用都会返回一个实际结果的真实承诺。没有运行时依赖关系,包支持 ESM 和 CommonJS 格式。安装命令:npm install @alsoftworks/temporize 为什么选择 temporize? - 功能: - temporize - lodash.debounce / lodash.throttle - 推断参数和解析返回值 - 完整的泛型推理 - 可单独获取的类型 - 每次调用返回 Promise<Awaited<R>> - 最后一个同步结果或 undefined - 异步错误传播 - 原生承诺拒绝 - 调用方管理返回值 - 取消功能:.cancel() 和 AbortSignal .cancel() - 最大防抖等待:是 / 是 - 批量队列速率限制器:throttlePromise / 否 - 异步重叠策略:debounceAsync / 否 - 动画帧节流:浏览器 API 加 Node 后备 - 多调用参数批处理:batch / 否 - 指数退避重试:retry / 否 - 空闲期调度:idle,带 Safari/Node 后备 / 否 - 并发承诺限制:concurrencyLimit,带 FIFO 队列 / 否 - 运行时依赖关系:零 / 方法包为零 - 模块:ESM 和 CommonJS / 方法包为 CommonJS 范围:temporal 故意集中在控制工作运行的时间和频率。防抖、节流、批处理、重试定时、帧调度和空闲调度都属于这个范畴。对象、数组、字符串和其他通用工具助手故意不在其范围之内;这个边界是设计决策,不是遗漏。 用法: - 防抖: ```javascript import { debounce } from "@alsoftworks/temporize"; const search = debounce( async (query: string) => { const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`); return response.json() as Promise<{ total: number }>; }, 250, { maxWait: 1_000 }, ); const result = await search("类型推断"); console.log(result.total); search.pending(); await search.flush(); search.cancel(); ``` 多个调用合并为一次调用,每一个都接收一个单独的承诺,与该调用的结果一起解决。仅前置防抖会用前置调用的结果解析被窗口压制的调用。 - 节流: ```javascript import { throttle } from "@alsoftworks/temporize"; const savePosition = throttle( (x: number, y: number) => ({ x, y, savedAt: Date.now() }), 100, { leading: true, trailing: true }, ); const saved = await savePosition(120, 80); ``` 常规节流合并多余调用并对后置调用使用最新参数。 - rafThrottle: ```javascript import { rafThrottle } from "@alsoftworks/temporize"; const updateLayout = rafThrottle((width: number) => { document.documentElement.style.setProperty("--viewport-width", `${width}px`); }); window.addEventListener("resize", () => updateLayout(window.innerWidth)); // 丢弃已请求但尚未运行的帧。 updateLayout.cancel(); ``` 在浏览器中,rafThrottle 使用 requestAnimationFrame。在 SSR 和 Node 中,它退回到 16 毫秒的 setTimeout,因此在没有动画帧全局的情况下导入和调用是安全的。 - debounceAsync: ```javascript import { debounceAsync } from "@alsoftworks/temporize"; const loadUser = debounceAsync( async (id: string, signal: AbortSignal) => { const response = await fetch(`/api/users/${id}`, { signal }); return response.json() as Promise<{ id: string; name: string }>; }, 200, { overlap: "cancel-previous" }, ); // AbortSignal 参数在内部提供,并在此调用中省略。 const user = await loadUser("user_123"); ``` 重叠策略有:“queue”(默认):保持每个触发的调用,并在活动工作结束后开始。在“drop”中:不开始重叠调用;其调用方采用活动调用的承诺。在 “cancel-previous”:中,立即启动新的调用并中止活动调用的内部提供信号。未声明信号的 JavaScript 函数安全忽略额外参数。对于类型信号注入,将所需的 AbortSignal 声明为包装函数的最后一个参数。debounceAsync 从返回函数的调用签名中移除了该参数。取消是协作的:异步操作必须观察信号以停止其进行中的工作。 - throttlePromise: ```javascript import { throttlePromise } from "@alsoftworks/temporize"; const sendRequest = throttlePromise( async (path: string) => fetch(path).then((response) => response.status), 100, ); const requests = [ sendRequest("/api/one"), sendRequest("/api/two"), sendRequest("/api/three"), ]; console.log(sendRequest.queued()); console.log(await Promise.all(requests)); ``` 每个调用进入 FIFO 队列,并至少在先前开始后的一个窗口后开始。队列限制调度速率,而不是并发:慢速承诺可能仍然在下一个窗口开始时处于活动状态。 - 批处理: ```javascript import { batch } from "@alsoftworks/temporize"; const markNotificationsRead = batch( // ... ); ```

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡