尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

eslint-plugin-unicorn 规则解析:用 `AbortSignal.timeout()` 取代手动 `AbortController` + `setTimeout()` 的定时取消模式

eslint-plugin-unicorn 规则解析:用 `AbortSignal.timeout()` 取代手动 `AbortController` + `setTimeout()` 的定时取消模式 eslint-plugin-unicorn 规则解析用AbortSignal.timeout()取代手动AbortControllersetTimeout()的定时取消模式【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicornprefer-abort-signal-timeout是 eslint-plugin-unicorn 中用于检查定时取消timeout abort代码模式的一条规则当代码用setTimeout()手动调用AbortController.abort()来到点取消一个异步操作时该规则会提示改用AbortSignal.timeout()。阅读完本文后你将掌握这条规则的确切匹配边界什么代码会被报告、什么代码会被有意忽略、它的编辑器建议suggestion修复行为、延迟参数的合法性校验逻辑以及如何在 ESLint 配置中启用或关闭它。本文的文档主体见 docs/rules/prefer-abort-signal-timeout.md实现源码见 rules/prefer-abort-signal-timeout.js。规则背景为什么推荐AbortSignal.timeout()在 Web 平台标准中AbortSignal.timeout()会直接创建一个延迟到达后自动 abort的AbortSignal省去了额外的AbortController实例与手动定时器的编排。文档原文的表述是AbortSignal.timeout()creates anAbortSignalthat automatically aborts after a delay. It avoids the extraAbortControllerand manual timer setup.换句话说这条规则瞄准的是用两条语句手搓定时取消的反模式// ❌ 手动的 AbortController setTimeout const abortController new AbortController(); setTimeout(() abortController.abort(), 1000); await fetch(url, {signal: abortController.signal});同样的语义可以用一条语句表达// ✅ 内置的 AbortSignal.timeout() const abortSignal AbortSignal.timeout(1000); await fetch(url, {signal: abortSignal});后者的优势在于不需要持有 controller、不需要维护定时器句柄、不会出现操作提前结束时定时器仍悬挂的泄漏隐患abort 原因reason也由运行时以超时语义统一管理。规则的报告范围只匹配简单相邻语句模式这条规则并非看到任何setTimeout(() controller.abort())都会报警。文档明确说明This rule only reports the simple adjacent-statement pattern where the controller is otherwise only used through.signal.从源码 rules/prefer-abort-signal-timeout.js 的createProblem()函数第 292–379 行可以还原出完整的匹配条件需要同时满足声明方式const abortController new AbortController();—— 必须是const关键字declaration.kind ! const直接跳过、单一变量声明、标识符为普通 Identifier并且new AbortController()无参数调用且AbortController是全局标识符第 297–304 行isGlobalAbortControllerConstructor见第 59–64 行。相邻语句紧随其后的下一条语句就是setTimeout(...)调用getNextStatement第 45–52 行且该调用恰好有 2 个参数、不使用可选链、setTimeout必须是全局标识符getTimeoutCall第 66–80 行。中间插入任何其他语句如prepare();都会使规则失效。回调形态setTimeout的第一个参数必须是箭头函数或普通函数表达式且不能是 async、不能是 generator、不能声明参数函数体要么是单个表达式controller.abort()要么是只含这一条表达式语句的块getCallbackExpression第 82–108 行。abort()调用必须无参数、无可选链、非计算属性getAbortReference第 110–131 行。controller 的唯一用途是.signal变量所有引用都必须是读取.signal成员getSignalMembers第 194–220 行——不能有对变量的写入/赋值、不能作为赋值左值、不能出现在for...of/for...in的左值位置isForLoopLeftSide第 133–138 行、不能把 signal 再赋给别的变量做别名isSignalAlias第 160–170 行。全局环境可用AbortSignal这个名字在当前作用域内未被遮蔽isGlobalNameAvailable第 54–57 行否则修复生成的AbortSignal.timeout()会引用错误对象。无注释干扰new AbortController()初始化表达式、TS 类型注解、声明与setTimeout之间、setTimeout语句本身及其行尾都不能有注释第 304–319 行避免破坏注释语义的修复。触发后报告位置落在声明语句的变量标识符上消息为PreferAbortSignal.timeout()over manually aborting anAbortControllerwithsetTimeout().故意忽略的场景检查 reason 或别名 signal 的代码规则的另一半是边界意识。AbortSignal.timeout()产生的 signal 携带的是超时专用 reasonTimeoutError而手动controller.abort()可以传入任意 reason。因此凡是依赖 abort reason 的用法都不适合机械替换规则会显式跳过// ✅ 有意忽略读取了 abort reason const abortController new AbortController(); setTimeout(() abortController.abort(), 1000); if (abortController.signal.reason) { handleAbort(); }// ✅ 有意忽略signal 被别名到其他变量后再使用 const abortController new AbortController(); setTimeout(() abortController.abort(), 1000); const signal abortController.signal; signal.throwIfAborted();源码中用reasonSensitiveProperties集合第 25–28 行枚举了 reason 敏感属性reason与throwIfAborted。isReasonSensitiveRead第 143–146 行负责识别这类成员读取——包括可选链形式signal?.reason和计算属性形式signal[reason]isSignalAlias则拦截signal 被赋给变量/作为赋值右值的别名用法。这些场景在文档中被概括为Cases that inspect the abort reason or alias the signal are ignored, asAbortSignal.timeout()uses a timeout-specific reason.此外文档还给了controller 有其他行为的忽略示例// ✅ 有意忽略除了定时 abort还有按钮点击也会 abort const abortController new AbortController(); setTimeout(() abortController.abort(), 1000); button.addEventListener(click, () abortController.abort()); await fetch(url, {signal: abortController.signal});这里的关键在于第 4 条匹配条件controller 除了.signal读取外还存在其他引用形式被addEventListener回调捕获getSignalMembers会因此返回undefined规则直接放行。修复行为编辑器建议suggestion如何改写代码该规则配置了hasSuggestions: truerules/prefer-abort-signal-timeout.js属于手动可修复由编辑器建议触发而非--fix自动应用suggestion 消息为 Replace withAbortSignal.timeout().。修复函数第 355–377 行依次执行四步变换替换初始化表达式把new AbortController()原地替换为AbortSignal.timeout(原延迟表达式文本)例如AbortSignal.timeout(1000)或AbortSignal.timeout(delay)改写 TS 类型注解如果变量声明带 TypeScript 注解如: AbortController会一并替换为: AbortSignal第 361–363 行智能重命名为了代码可读性abortController会重命名为abortSignalcontroller会重命名为signalgetReplacementName第 269–290 行但若目标名字在作用域内已存在同名变量如已有abortSignal则保留原名字以免冲突——快照 test/snapshots/prefer-abort-signal-timeout.js.md 展示了这一行为当const signal getSignal();已存在时controller不被重命名最终输出const controller AbortSignal.timeout(delay);删除定时器语句并改写引用移除整条setTimeout(...)语句借助 rules/fix/index.js 的removeStatement并把所有.signal成员引用替换为新的变量名。快照中的实际输出示例对应invalid(3)见 test/snapshots/prefer-abort-signal-timeout.js.md// 输入 const abortController new AbortController(); setTimeout(() abortController.abort(), delay); fetch(url, {signal: abortController.signal}); // 建议修复后的输出 const abortSignal AbortSignal.timeout(delay); fetch(url, {signal: abortSignal});即使 controller 声明在switch的 case 块内部修复同样成立见invalid(2)的快照第 31–68 行TypeScript 场景下const abortController: AbortController new AbortController();会被改写为const abortSignal: AbortSignal AbortSignal.timeout(delay);invalid(8)第 213–235 行。延迟参数的合法性校验setTimeout与AbortSignal.timeout()的语义差异手动方案里setTimeout(() abort(), 1000)的第二个参数由setTimeout语义处理而AbortSignal.timeout(delay)对 delay 的要求更严格两者并不完全等价。源码在isValidAbortSignalTimeoutDelay第 238–257 行与shouldSkipDelay第 259–262 行中做了三道防线禁止SequenceExpressionsetTimeout(fn, (first(), second))这类逗号表达式无法原样搬进AbortSignal.timeout()直接跳过静态值范围校验如果延迟表达式可静态求值且无副作用则要求结果为 number、是安全整数Number.isSafeInteger、且满足0 value MAX_SET_TIMEOUT_DELAY。其中MAX_SET_TIMEOUT_DELAY (2 ** 31) - 1 2147483647第 24 行对应setTimeout的最大延迟——超出该值setTimeout会钳制为 1ms语义与AbortSignal.timeout()不同故跳过。测试覆盖了-1、1.5、100、2147483648、4294967295等非法值见 test/prefer-abort-signal-timeout.js边界值2147483647则是合法的invalid(10)动态表达式保守放行无法静态求值的动态延迟如options.timeout、(timeout extraDelay)假定用户已确保其在未钳制的setTimeout范围内有效规则不再深究文档原话Dynamic delay expressions are assumed to already be validAbortSignal.timeout()delays within the unclampedsetTimeout()range。但若延迟表达式带潜在可变成员访问如 getter 可能返回非法值getStaticValueIfNoSideEffects与getStaticValue的联合判断第 239–246 行会拒绝报告若 delay 表达式内引用了abortController.signal如getDelay(abortController.signal)也会因isInsideRange检查第 259–262 行被跳过。配置方式recommended 默认开启schema 无选项该规则在元数据中声明recommended: truerules/prefer-abort-signal-timeout.js因此它被包含在插件的 ✅recommended配置中默认启用良好实践而在 ☑️unopinionated配置中为disabled状态。这一信息也记录在规则文档头部docs/rules/prefer-abort-signal-timeout.md。由于meta.schema为空数组第 400 行规则不接受任何选项只能开关不能调参// eslint.config.jsflat config import unicorn from eslint-plugin-unicorn; import {defineConfig} from eslint/config; export default defineConfig([ { files: [**/*.js], plugins: {unicorn}, extends: [unicorn/recommended], rules: { // 显式关闭 unicorn/prefer-abort-signal-timeout: off, // 或显式提升为 error unicorn/prefer-abort-signal-timeout: error, }, }, ]);recommended配置的完整用法可参考 readme.md 的 Preset configs 章节规则清单在 readme.md 中标记为 ✅recommended且带 提供 suggestion。规则本身在 rules/index.js 中统一注册。测试验证覆盖边界行为的快照用例规则的边界行为由 test/prefer-abort-signal-timeout.js 的 50 余组用例背书并通过 AVA 快照固化输出。valid 用例覆盖了规则的所有不越界场景例如已直接使用AbortSignal.timeout(delay)定时器之外还存在手动abortController.abort()调用abort(reason)带参数AbortController/AbortSignal/setTimeout被本地变量遮蔽async / generator / 带参数的回调setTimeout?.(...)可选链调用、controller?.abort()、controller.signal?.xlet声明、声明后被重新赋值、一条const里声明多个变量读取.aborted非 reason 敏感以外.reason、throwIfAborted()、解构const {reason} abortController.signal、signal别名后使用均被忽略各种注释位置、delete abortController.signal、for...of/for...in左值、({signal: abortController.signal} options)解构赋值。invalid 用例则验证了相邻语句 仅.signal使用模式的修复产出全部与 test/snapshots/prefer-abort-signal-timeout.js.md 中的快照一一对应可作为迁移时的参考金样。小结prefer-abort-signal-timeout是一条克制而精准的规则它只在const 声明紧邻setTimeout定时 abort、controller 仅通过.signal消费、且不涉及 abort reason这一简单模式下给出建议并借助编辑器 suggestion 完成包括 TS 注解改写、变量智能重命名、定时器语句清除在内的安全重构同时通过延迟静态值校验与全局标识符检查避免在setTimeout与AbortSignal.timeout()语义不等价的场景下误报。如果你的项目追求更简洁的定时取消写法在recommended配置下直接享受这条规则即可无需额外选项配置。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表