{"_id":"@azsxdc12356/utils","_rev":"6-dfa8442aa21b0212b87c90ad21839a61","name":"@azsxdc12356/utils","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.0":{"name":"@azsxdc12356/utils","version":"1.0.0","keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","_id":"@azsxdc12356/utils@1.0.0","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"dist":{"shasum":"af82f5f780b032252b0c1a1390a3cf21777d6df8","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.0.tgz","fileCount":8,"integrity":"sha512-JG4rLW/+hFcV/j/6r1MUy75GVSdHpmr/vtPg12nkNmDwbIMOYDjvaolM+pIiNKKNXTf9OvA+jF4R3oO5uy0sGA==","signatures":[{"sig":"MEQCIE64dngpw2mqiZ4aeYoWCM08NXLolWTRusiv64wOugrlAiA4iSDF4vB1bR468g+tCMfnrMk6JUnFpxkJFUmeiOMEGw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21433},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"}},"gitHead":"4d212e3452d5dc675cdfb380375598b3ce7f617b","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write src/","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","prepublishOnly":"npm run build"},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"repository":{"url":"git+https://github.com/junjie-z666/utils.git","type":"git"},"_npmVersion":"10.1.0","description":"TypeScript utility functions and React Hooks","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^29.1.1","react":"^19.2.6","eslint":"^10.4.0","vitest":"^4.1.7","prettier":"^3.8.3","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/react":"^19.2.15","typescript-eslint":"^8.59.4","@vitejs/plugin-react":"^6.0.2","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_1.0.0_1779603429064_0.5805996935481825","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@azsxdc12356/utils","version":"1.0.1","keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","_id":"@azsxdc12356/utils@1.0.1","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"dist":{"shasum":"8c996ee276eaa30d5f1ab2af49a1b4f08e39161c","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.1.tgz","fileCount":8,"integrity":"sha512-iTHt21pn1Fd1UDUOZDCqpGS0mT0Bpi4Wic5ojvNmJ+YxJ48O+i3eqd+wl82eM7+gdThm9hyxw72u9fNyDh001w==","signatures":[{"sig":"MEQCIDiA11p2bw9dEbfxA1zHEzvn1pI3QdZUnRn4gY+KFNYRAiB8Aekd5/2GMoZyl7SnYno5NWz9xcG38I/CTihkKNiNPw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21586},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"}},"gitHead":"4d212e3452d5dc675cdfb380375598b3ce7f617b","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write src/","publish":"npm publish --access public --registry https://registry.npmjs.org","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","prepublishOnly":"npm run build"},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"repository":{"url":"git+https://github.com/junjie-z666/utils.git","type":"git"},"_npmVersion":"10.1.0","description":"TypeScript utility functions and React Hooks","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.0","react":"^19.2.6","eslint":"^9.0.0","vitest":"^3.0.0","prettier":"^3.8.3","@eslint/js":"^9.0.0","typescript":"^5.7.0","@types/react":"^19.2.15","typescript-eslint":"^8.59.4","@vitejs/plugin-react":"^4.3.0","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_1.0.1_1779607278214_0.6184640206812857","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@azsxdc12356/utils","version":"1.0.2","keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","_id":"@azsxdc12356/utils@1.0.2","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"dist":{"shasum":"690f61029c3dba81f5934a94270c26ab823b22f7","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.2.tgz","fileCount":8,"integrity":"sha512-R7ELgONmlxX3hs1Xu1LK1dK/k7PFIQSOqmh7+5sGVp2VBjrvnrgw+1PEWuKdDl5ogu99RzKl+V3METDKZqEKow==","signatures":[{"sig":"MEUCIQCW0sPGrwEMI6ErAPhH9a+7GNHTzq8bQHfE1mEWNhhccgIgQngZXY1b7SjmA9g8jIwZUlR6+4x2uqIIio6frwGbfWI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21620},"type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"}},"gitHead":"66ab82445e9e4a6fdd74be5fe13965bb579b73e9","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write src/","lint:fix":"eslint src/ --fix","pushToNpm":"npm publish --access public --registry https://registry.npmjs.org","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","prepublishOnly":"npm run build"},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"repository":{"url":"git+https://github.com/junjie-z666/utils.git","type":"git"},"_npmVersion":"10.1.0","description":"TypeScript utility functions and React Hooks","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.0","react":"^19.2.6","eslint":"^9.0.0","vitest":"^3.0.0","prettier":"^3.8.3","@eslint/js":"^9.0.0","typescript":"^5.7.0","@types/react":"^19.2.15","typescript-eslint":"^8.59.4","@vitejs/plugin-react":"^4.3.0","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_1.0.2_1779609143782_0.5443832832138831","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@azsxdc12356/utils","version":"1.0.3","keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","_id":"@azsxdc12356/utils@1.0.3","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"dist":{"shasum":"05ebb9deddba045a80380b2c7ad87ae6f90e6e93","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.3.tgz","fileCount":8,"integrity":"sha512-MFURfOdiSv7Iu4MuJpiITmA9spn5mYkTwBEynUPnnYB1t3tW84J67mpWDYs0X3b/4WtGJifRTbH2WAXQSQB3gw==","signatures":[{"sig":"MEQCIAKdAAYTROHZqBa3T9MHrsb06a87CO6T89tf1mmjkiXGAiBLWRtD2MZPGanN+tAKG98PMdBW+ptA+BwhwoXRzTNlRA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22509},"type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"}},"gitHead":"aa2063619579b08c9a2dc04f2781b25e641984e8","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write src/","lint:fix":"eslint src/ --fix","pushToNpm":"npm publish --access public --registry https://registry.npmjs.org","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","prepublishOnly":"npm run build"},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"repository":{"url":"git+https://github.com/junjie-z666/utils.git","type":"git"},"_npmVersion":"10.1.0","description":"TypeScript utility functions and React Hooks","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.0","react":"^19.2.6","eslint":"^9.0.0","vitest":"^3.0.0","prettier":"^3.8.3","@eslint/js":"^9.0.0","typescript":"^5.7.0","@types/react":"^19.2.15","typescript-eslint":"^8.59.4","@vitejs/plugin-react":"^4.3.0","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_1.0.3_1779630422101_0.6030593256243078","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@azsxdc12356/utils","version":"1.0.4","keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","_id":"@azsxdc12356/utils@1.0.4","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"dist":{"shasum":"695c044ebecc8cdd7b2296cd0e4826af1de5aa8d","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.4.tgz","fileCount":8,"integrity":"sha512-0PNo+fT5J8MSWJNnml/qvwAE7SFsGuEUP9mqzd9/sV2uK7jOI8ZJuAq2fG7lbiPk6/wCYcTaniL3cuLGyj2JmA==","signatures":[{"sig":"MEUCIAjIt9JR9NaV/MbyCpllz5pFagPtbYdI4/noOyhtyDPcAiEA+JQmCwuP+Xr0tIlm8APFcJBq2Ut4CPd/fVDLlNzqfec=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22509},"type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"}},"gitHead":"469700a1d3c960a3d2f76853ce28b64d936ebb73","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","format":"prettier --write src/","lint:fix":"eslint src/ --fix","pushToNpm":"npm publish --access public --registry https://registry.npmjs.org","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","prepublishOnly":"npm run build"},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"repository":{"url":"git+https://github.com/junjie-z666/utils.git","type":"git"},"_npmVersion":"10.1.0","description":"TypeScript utility functions and React Hooks","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.0","react":"^19.2.6","eslint":"^9.0.0","vitest":"^3.0.0","prettier":"^3.8.3","@eslint/js":"^9.0.0","typescript":"^5.7.0","@types/react":"^19.2.15","typescript-eslint":"^8.59.4","@vitejs/plugin-react":"^4.3.0","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_1.0.4_1779980655796_0.02603542695905836","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@azsxdc12356/utils","version":"1.0.5","description":"TypeScript utility functions and React Hooks","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./hooks":{"types":"./dist/hooks/index.d.ts","import":"./dist/hooks/index.js"},"./mockMiddleWare":{"types":"./dist/node/mockMiddleWare.d.ts","import":"./dist/node/mockMiddleWare.js"}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","lint":"eslint src/","lint:fix":"eslint src/ --fix","format":"prettier --write src/","format:check":"prettier --check src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","pushToNpm":"npm publish --access public --registry https://registry.npmjs.org"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"keywords":["typescript","utils","hooks","react","promise","timer"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/junjie-z666/utils.git"},"homepage":"https://github.com/junjie-z666/utils","bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"devDependencies":{"@eslint/js":"^9.0.0","@testing-library/react":"^16.3.2","@types/react":"^19.2.15","@vitejs/plugin-react":"^4.3.0","eslint":"^9.0.0","jsdom":"^25.0.0","prettier":"^3.8.3","react":"^19.2.6","tsup":"^8.5.1","typescript":"^5.7.0","typescript-eslint":"^8.59.4","vitest":"^3.0.0"},"_id":"@azsxdc12356/utils@1.0.5","gitHead":"557008ea9efc076408ed7b738a3a032ab1263982","_nodeVersion":"22.14.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-6BM19USq8eKprLYP3DMbQFsVcMisnY5yY5s++YgIdodvJA9q1OYCoZS5GZS4gWB+WM8Re/8SXDCzQRqtkGqMiQ==","shasum":"56afbb83e22a7e5ac2e71484f588c1811363e862","tarball":"https://registry.npmjs.org/@azsxdc12356/utils/-/utils-1.0.5.tgz","fileCount":10,"unpackedSize":41262,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCWRvGNjkfJuNgzfx7U0f/5kvIVCU2KdJbbD5tO3OqF9AIgRVb1FU9Rqv4Q8UFM9c6SIodoYSN4RaL7wKXZM9GUgwg="}]},"_npmUser":{"name":"azsxdc12356","email":"527759787@qq.com"},"directories":{},"maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_1.0.5_1779981779314_0.5511855652392188"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-24T06:17:08.937Z","modified":"2026-05-28T15:22:59.577Z","1.0.0":"2026-05-24T06:17:09.223Z","1.0.1":"2026-05-24T07:21:18.377Z","1.0.2":"2026-05-24T07:52:23.942Z","1.0.3":"2026-05-24T13:47:02.264Z","1.0.4":"2026-05-28T15:04:15.931Z","1.0.5":"2026-05-28T15:22:59.460Z"},"bugs":{"url":"https://github.com/junjie-z666/utils/issues"},"license":"MIT","homepage":"https://github.com/junjie-z666/utils","keywords":["typescript","utils","hooks","react","promise","timer"],"repository":{"type":"git","url":"git+https://github.com/junjie-z666/utils.git"},"description":"TypeScript utility functions and React Hooks","maintainers":[{"name":"azsxdc12356","email":"527759787@qq.com"}],"readme":"# @azsxdc12356/utils\n\nTypeScript 工具函数和 React Hooks 集合。\n\n## 安装\n\n```bash\nnpm install @azsxdc12356/utils\n```\n\n## 目录\n\n| 工具 | 类型 | 简介 |\n| --- | --- | --- |\n| `AsyncOnce` | 异步轮询器 | 快速连续调用同一异步操作，只回调最后一次结果，丢弃过期结果 |\n| `AsyncInterval` | 异步轮询器 | 固定间隔轮询，上次未完成则跳过本次，不会并发堆积 |\n| `AsyncTimeout` | 异步轮询器 | 上次完成后等待固定间隔再执行，请求之间不重叠 |\n| `createSharedStateHook` | React Hook | 无需 Provider 的跨组件共享状态，多组件调用同一 hook 即可共享 |\n| `createCanceledPromise` | 工具函数 | 给 Promise 加 cancel 方法，可主动取消并区分取消与错误 |\n| `ConcurrencyQueue` | 工具函数 | 并发控制队列，限制同时执行的任务数，超出排队等待 |\n| `createSinglePromise` | 工具函数 | 单例 Promise，多次调用共享同一个正在执行的 promise，不重复执行 |\n| `setupMock` | Mock 中间件 | Vue CLI devServer mock 中间件，基于目录结构自动发现接口，支持热更新和空文件自动抓取 |\n\n## 使用\n\n### 异步轮询器\n\n```ts\nimport { AsyncOnce, AsyncInterval, AsyncTimeout } from '@azsxdc12356/utils'\n```\n#### 共同特性\n\n三者都继承自 `AsyncPolling` 基类，具有以下特性：\n\n- **重新 `start` 时旧结果不回调**：运行中途重新调用 `start`，上一次请求的结果会被丢弃\n- **`stop` 后结果不回调**：调用 `stop` 后，正在执行的请求结果不会回调\n- **`setCallback` 动态更新回调**：运行过程中可以替换回调函数\n\n\n#### `AsyncOnce` — 只取最后一次\n\n快速连续调用同一异步操作时，只回调最后一次的结果，前几次的过期结果会被丢弃。\n\n**解决痛点**：搜索框输入、按钮连点等场景下快速触发同一异步操作，前面请求的结果回来后会覆盖最新结果或导致 UI 闪烁。\n\n\n```ts\nconst once = new AsyncOnce<string, string>(\n  async (url) => fetch(url).then(r => r.text()),\n  (result) => console.log(result)\n)\n\nonce.start('/api/1')  // 还没返回\nonce.start('/api/2')  // 还没返回\nonce.start('/api/3')  // 只有这次的结果会回调\n```\n\n\n#### `AsyncInterval` — 固定间隔轮询（上一个没完不执行下一个）\n\n按固定间隔执行异步操作，如果上一次还没返回则跳过本次，不会并发堆积。\n\n**解决痛点**：原生 `setInterval` 不关心上次请求是否完成，响应慢时请求会并发堆积。AsyncInterval 保证不并发，且 `stop()` 和重新 `start()` 时旧结果不会回调。\n\n\n```ts\nconst interval = new AsyncInterval<string, Data>(\n  3000,\n  async (param) => fetchData(param),\n  (result) => console.log(result)\n)\n\ninterval.start('param')  // 立即执行一次，之后每 3s 检查一次，完成了再执行，不完成跳过。\n// ...\ninterval.stop()          // 停止轮询，正在执行的请求结果不会回调\n```\n\n#### `AsyncTimeout` — 上次完成后等固定间隔再执行\n\n上一次异步操作完成后，等待固定间隔再执行下一次，请求之间不会重叠。\n\n**解决痛点**：`setInterval` 不管请求耗时，如果请求耗时 > 间隔就会堆积。AsyncTimeout 保证上一次完成后才开始计时，适合接口响应时间不确定的轮询场景。\n\n```ts\nconst timeout = new AsyncTimeout<string, Data>(\n  3000,\n  async (param) => fetchData(param),\n  (result) => console.log(result)\n)\n\ntimeout.start('param')  // 立即执行一次，完成后等 3s 再执行下一次\n// ...\ntimeout.stop()          // 停止轮询\n```\n### React Hooks\n\n```ts\nimport { createSharedStateHook } from '@azsxdc12356/utils'\n```\n\n#### `createSharedStateHook` — 无需 Provider 的跨组件共享状态\n\n创建一个 hook，多个组件调用同一个 hook 即可共享状态，无需 Context + Provider 包裹。\n\n**解决痛点**：跨组件共享状态通常需要引入 Context + Provider 或状态管理库，对于简单场景太重了。`createSharedStateHook` 在模块顶层创建，组件直接调用即可共享，无需包裹 Provider。\n\n\n```tsx\nconst useUserInfo = createSharedStateHook({ name: '', age: 0 })\n\nfunction Header() {\n  const [user, setUser] = useUserInfo()\n  return <Text>{user.name}</Text>\n}\n\nfunction Editor() {\n  const [user, setUser] = useUserInfo()\n  return (\n    <TextInput\n      value={user.name}\n      onChangeText={(text) => setUser({ ...user, name: text })}\n    />\n  )\n}\n```\n\n**`onlyUpdate` 选项**：只获取 setter 不订阅更新，适合只需要写不需要读的场景（避免不必要的重渲染）。\n\n```tsx\nconst [, setUser] = useUserInfo({ onlyUpdate: true })\n```\n\n\n### 工具函数\n\n```ts\nimport { createCanceledPromise, ConcurrencyQueue, createSinglePromise } from '@azsxdc12356/utils'\n```\n\n#### `createCanceledPromise` — 可取消的 Promise\n\n给 Promise 加上 cancel 方法，调用后返回的 promise 会 reject 并携带 `{ canceled: true }`，用户可据此区分取消和正常错误。\n\n**解决痛点**：原生 Promise 一旦创建无法取消。网络请求、页面跳转等场景需要中断正在进行的异步操作，否则结果回来后会更新已不存在的 UI 或引发报错。\n\n\n```ts\nconst cancelable = createCanceledPromise(fetchData(), () => abortController.abort())\n\ntry {\n  const data = await cancelable\n} catch (e) {\n  if (e?.canceled) {\n    // 用户主动取消\n  } else {\n    // 真正的错误\n  }\n}\n\n// 需要取消时\ncancelable.cancel()\n```\n\n\n#### `ConcurrencyQueue` — 并发控制队列\n\n控制同时执行的任务数量，超出并发上限的任务排队等待，完成一个自动执行下一个。\n\n**解决痛点**：批量发起网络请求（上传文件、批量下载）时，不加控制会瞬间创建大量并发连接，导致浏览器卡顿或服务端拒绝。ConcurrencyQueue 让并发数始终在可控范围内。\n\n\n```ts\nconst queue = new ConcurrencyQueue('upload', 3)\n\nqueue.addItem({\n  id: 'file1',\n  start: () => uploadFile('file1'),\n})\nqueue.addItem({\n  id: 'file2',\n  start: () => uploadFile('file2'),\n})\n\n// 移除排队中的任务\nqueue.removeItem('file2')\n// 清空所有排队任务\nqueue.removeAll()\n```\n\n\n#### `createSinglePromise` — 单例 Promise\n\n多次调用 `get()` 时，如果上一次还没完成，直接返回正在执行的 promise，不会重复执行。\n\n**解决痛点**：多个模块同时请求同一份配置/资源，不加控制会发出多个重复请求。createSinglePromise 保证同一时刻只有一个请求，所有调用者共享同一个 promise。\n\n\n```ts\nconst singleGetConfig = createSinglePromise(() => fetchConfig())\n\n// 多处同时调用，只会发一次请求\nconst config1Promise = singleGetConfig.get()\nconst config2Promise = singleGetConfig.get()\n```\n\n### Mock 中间件\n\n```ts\nimport { setupMock } from '@azsxdc12356/utils/mockMiddleWare'\n```\n\n#### `setupMock` — Vue CLI devServer mock 中间件\n\n供各 app 共用的 mock 中间件，基于目录结构自动发现 mock 接口文件，支持文件监听热更新和空文件自动抓取真实数据。\n\n**启用方式**：设置环境变量 `VUE_APP_MOCK=true` 即可启用，未设置时所有请求正常走代理。\n\n```bash\nVUE_APP_MOCK=true vue-cli-service serve\n```\n\n**在 vue.config.js 中接入**：\n\n```js\nconst { setupMock } = require(\"../mock\")\n\nmodule.exports = {\n  devServer: {\n    proxy: {\n      \"/api\": {\n        target: \"http://example.com\",\n        changeOrigin: true,\n      },\n    },\n    onBeforeSetupMiddleware(devServer) {\n      setupMock(devServer, __dirname, [\"/api\"])\n    },\n  },\n}\n```\n\n参数说明：\n- `devServer` — webpack-dev-server 实例\n- `appRoot` — app 根目录绝对路径，用于定位 `./mock` 子目录\n- `proxyPaths` — 需要拦截的接口路径前缀数组，顺序必须与 `devServer.proxy` 配置一致\n\n**Mock 文件约定**：在 app 的 `mock/` 目录下按接口路径创建 `.json` 文件，目录层级 = 接口路径层级。\n\n```\nmock/api/xxx/yyy/zzz.json   →  POST /api/xxx/yyy/zzz\n```\n\n- 文件路径（去掉 `mock/` 前缀和 `.json` 后缀）= 接口完整路径\n- 请求方法不限（GET/POST 均匹配同一文件）\n- 返回数据会自动注入 `\"mock\": true` 标记字段\n- 找不到对应 `.json` 文件的请求，自动 fallback 到 `devServer.proxy` 代理\n\n**空文件自动抓取**：创建一个 0 字节的空 `.json` 文件，首次请求时自动转发到真实代理接口获取 response 并写入文件，后续请求直接返回 mock 数据。重新抓取只需清空文件内容。\n\n**文件监听热更新**：`mock/` 目录下的 `.json` 文件增删改会自动触发重新收集，无需重启 dev server。\n\n## License\n\nMIT\n","readmeFilename":"README.md"}