eslint-plugin-unicorn 的 prefer-abort-signal-any 规则:用 `AbortSignal.any()` 替代手动转发 abort 事件

发布时间:2026/9/18 5:31:22

eslint-plugin-unicorn 的 prefer-abort-signal-any 规则:用 `AbortSignal.any()` 替代手动转发 abort 事件 eslint-plugin-unicorn 的 prefer-abort-signal-any 规则用AbortSignal.any()替代手动转发 abort 事件【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读prefer-abort-signal-any是 eslint-plugin-unicorn 提供的一条代码风格规则rule type 为suggestion它检测代码中手动创建AbortController并逐个监听输入信号的abort事件来转发中止信号的桥接模式建议改用标准 APIAbortSignal.any()组合多个信号。本文基于 docs/rules/prefer-abort-signal-any.md 展开并结合 rules/prefer-abort-signal-any.js 的源码实现与 test/prefer-abort-signal-any.js 的测试用例深入说明该规则的触发条件、忽略场景、编辑器建议修复suggestion的生成逻辑及其在 JavaScript / TypeScript 下的行为差异帮助你在真实项目中理解并安全地启用这条规则。规则背景为什么推荐AbortSignal.any()AbortSignal.any()是 Web 平台提供的一个静态方法它会创建一个新的AbortSignal当传入的任意一个输入信号被中止时该组合信号也随之中止。相比手写桥接逻辑它有两大优势省去多余的AbortController不再需要中转控制器来统一管理多个来源的中止省去手动转发事件不再需要为每个输入信号分别addEventListener(abort, ...)并把事件转发到控制器。在AbortController/AbortSignal成为浏览器与 Node.js 通用基础设施fetch、流、定时器等的今天AbortSignal.any()是组合多个中止信号的标准做法。启用状态该规则在 ✅recommended配置中启用在 ☑️unopinionated配置中禁用。这条配置事实与规则元数据一致——源码 rules/prefer-abort-signal-any.js 中docs.recommended为true。修复方式本规则不提供自动修复fix而是通过编辑器建议suggestion手动触发修复对应元数据中的hasSuggestions: true。规则触发的典型模式规则针对的是源码中getDirectBridge直接桥接与getForOfBridge循环桥接两种可识别的桥接结构。下面先看规则文档给出的核心示例。循环转发for-of 桥接——应报告// ❌ const abortController new AbortController(); for (const signal of signals) { signal.addEventListener(abort, () abortController.abort()); } await fetch(url, {signal: abortController.signal});// ✅ const abortSignal AbortSignal.any(signals); await fetch(url, {signal: abortSignal});逐条转发直接桥接——应报告// ❌ const abortController new AbortController(); firstSignal.addEventListener(abort, () abortController.abort()); secondSignal.addEventListener(abort, () abortController.abort()); await fetch(url, {signal: abortController.signal});// ✅ const abortSignal AbortSignal.any([firstSignal, secondSignal]); await fetch(url, {signal: abortSignal});注意两种模式的修复产物略有差异for-of 模式直接使用循环所迭代的数组变量AbortSignal.any(signals)而直接桥接模式则把逐条监听的对象收集成数组字面量AbortSignal.any([firstSignal, secondSignal])。从源码看这一行为由 getForOfBridge 与 getDirectBridge 分别构造 replacement 文本实现。带其他行为的控制器被有意忽略规则非常保守只有当AbortController的唯一用途就是通过.signal把合并结果传给消费方时才值得替换。文档明确给出了一个控制器还有其他用途的忽略示例// ✅ const abortController new AbortController(); signal.addEventListener(abort, () abortController.abort()); button.addEventListener(click, () abortController.abort()); await fetch(url, {signal: abortController.signal});这里控制器除了响应signal的 abort 事件外还被按钮点击事件主动调用abort()因此被规则视为有其他行为不会报告。这与源码中getSignalMembers的检查一致它要求变量abortController的所有引用要么是桥接事件里的abort()调用要么是.signal成员访问getSignalMembers一旦出现其他引用方式如赋值写入、别名、abortController.abort()的直接调用就会放弃报告。报告的限定条件只处理简单模式规则文档强调它只报告控制器除此之外仅通过.signal使用的简单模式。具体而言以下情况会被忽略对应源码中的getSignalMember守卫见 rules/prefer-abort-signal-any.js检查组合信号的 abort reason如读取abortController.signal.reason或调用abortController.signal.throwIfAborted()。原因在于AbortSignal.any()保留的是第一个被中止的输入信号的 reason而手动桥接通常不会等价地保留 reason甚至可能把 reason 丢失。源码用reasonSensitiveProperties集合reason、throwIfAborted识别这类读取reasonSensitiveProperties对信号做别名如const signal abortController.signal;后再使用规则无法可靠地追踪别名后续的读写对.signal成员做写入或作为 for-in/for-of 左值在控制器声明或.signal相关代码附近存在注释源码通过hasCommentInRange、isStatementCommentFree等检查确保被删除/改写的代码中没有可能承载语义的注释hasCommentBetween输入信号无法被确认为AbortSignal例如对普通EventTarget实例注册abort监听器或对AbortSignal.abort()这类已知已中止的信号再转发后者在组合结果中会改变AbortSignal.any()的 reason 语义均不会报告相关检查见 hasKnownAlreadyAbortedSignal 与 isDirectBridgeSource。建议修复suggestion的生成细节当规则命中时它不会自动改写代码而是抛出一条带建议的报错报错信息Prefer AbortSignal.any() over manually forwarding abort events between signals.建议信息Replace with AbortSignal.any().修复逻辑在 createProblem 的suggest.fix中完成大致包括四步用AbortSignal.any(...)表达式替换new AbortController()初始化如果声明带有 TypeScript 类型注解则把类型改为: AbortSignal变量名abortController→abortSignal、controller→signal的重命名会先通过hasNameConflict检查新名字是否与作用域内已有变量冲突冲突则保留原名见 getReplacementName删除所有桥接的addEventListener语句removeStatementGroup会连同整行空白一起清理见 removeStatementGroup。测试快照 test/snapshots/prefer-abort-signal-any.js.md 展示了实际修复输出例如对下面的输入const abortController new AbortController(); firstSignal.addEventListener(abort, () abortController.abort()); secondSignal.addEventListener(abort, () abortController.abort()); fetch(url, {signal: abortController.signal});建议修复后的代码为const abortSignal AbortSignal.any([firstSignal, secondSignal]); fetch(url, {signal: abortSignal});可被识别的输入信号来源为了让替换在语义上安全规则只接受确定是 AbortSignal的输入来源。从源码可以归纳出以下几类被接受的形式标识符包括变量名以signal结尾或恰为signal例如firstSignal、secondSignalisSignalLikeNameAbortSignal静态调用AbortSignal.timeout(...)这类调用可直接作为输入快照 invalid(2) 即验证了AbortSignal.any([AbortSignal.timeout(1000), secondSignal])的修复数组字面量 /Array.of/new Array/Array(...)/ 单参数Array.fromfor-of 桥接中只要迭代源是这些可静态展开的数组形态且元素均为直接桥接源就可以被合并相关实现见 getKnownArrayElements 与 isAllowedArrayCompositionSource。反之以下来源会导致放弃报告测试的 valid 用例中有大量对应覆盖迭代源是Set、Array.from带映射回调、new Set等不可静态分析的集合元素个数为 0 或 1组合没有意义数组中混入abortController.signal自身形成自引用信号来自函数调用如getSignal()等。TypeScript 支持类型感知与只读数组规则对 TypeScript 提供了两层支持语法层在未开启类型检查parser 无program时通过类型注解推断例如signals as AbortSignal[]、readonly AbortSignal[]、元组[AbortSignal, AbortSignal]、as const断言等都会被识别。当类型注解明确是EventTarget[]时如 valid 用例function compose(signals: EventTarget[])则判定输入不是 AbortSignal不会误报类型感知层当通过parserOptions.projectService等开启类型信息后测试中的typeAware用例会调用 TypeScript 的getTypeChecker沿类型图联合类型、交叉类型、类型约束、基类递归判断某表达式是否为AbortSignal或AbortSignal[]核心实现在 isAbortSignalType 与 isReadonlyArrayType。此外针对只读数组readonly AbortSignal[]、ReadonlyArrayAbortSignal、元组、as const修复时会生成数组拷贝[...signals]以避免AbortSignal.any()的规范行为它不持有输入数组引用但拷贝能防御只读语义下的后续变异。这一逻辑集中在 needsArrayCopyForAbortSignalAny 与 getAbortSignalAnyArgumentText并由 isForOfArray 决定 for-of 桥接是否成立。桥接回调的形态约束规则对addEventListener(abort, callback)的回调做了严格限定只有满足以下条件才被认为是纯转发getCallbackExpression 与 getAbortReference回调必须是箭头函数或普通函数表达式不能是 async 或 generator 函数回调不能有参数带event参数的回调不匹配回调体只能是单个表达式或只含一条表达式语句的块表达式必须是abortController.abort()可选地携带sourceSignal.reason如abortController.abort(firstSignal.reason)——规则认可这种显式转发 reason的形式因为它在语义上等价于AbortSignal.any()的 reason 行为监听器的第三个参数options只能是布尔字面量或只含capture/once/passive布尔字面量的对象或直接省略isAllowedListenerOptions。这些约束在 test/prefer-abort-signal-any.js 的 valid / invalid 用例中都有体现例如async () abortController.abort()、event abortController.abort()、回调内附带cleanup()、firstSignal?.addEventListener(...)可选链等均被视为不安全而放行。如何在项目中使用启用规则该规则已包含在recommended配置中直接使用 unicorn 的推荐配置即可// eslint.config.jsflat config import unicorn from eslint-plugin-unicorn; export default [ unicorn.configs[flat/recommended], ];或在传统.eslintrc中{ extends: [plugin:unicorn/recommended] }如果你不希望启用该规则例如项目需要兼容不支持AbortSignal.any()的老环境可以显式关闭{ rules: { unicorn/prefer-abort-signal-any: off } }该规则无任何可配置项schema: []默认即开启。需要说明的是AbortSignal.any()是较新的 Web API启用前请确认目标运行环境支持该静态方法。规则不适用于哪些场景综合文档与源码以下场景不会被报告可放心使用控制器还被按钮、定时器等其他来源主动调用abort()组合信号的 reason 或throwIfAborted()被消费信号被别名引用、赋值给新变量或写入成员桥接语句之间或控制器声明处存在注释规则宁可放过也不破坏注释语义输入信号无法被静态确认为AbortSignal尤其是类型感知模式下被明确标注为EventTarget的情况已知已中止的信号AbortSignal.abort()结果被作为桥接输入。小结prefer-abort-signal-any是 eslint-plugin-unicorn 中一条少即是多的现代化规则它只瞄准一个非常具体的反模式——用AbortController手工桥接多个AbortSignal的 abort 事件——并以编辑器建议的形式安全地改写为AbortSignal.any()。从 源码实现 与 测试快照 可以看到它在识别桥接模式、处理 reason 语义、支持 TypeScript 类型推断以及生成安全修复方面做了大量保守而精细的守卫因此在启用recommended配置的仓库中可以放心让这条规则帮你持续清理手写的中止转发样板代码。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/18 5:26:22

Cocos Creator从入门到打包APK:2D游戏开发完整实战指南

Cocos Creator 这个引擎,这几年在2D手游和小游戏领域几乎是绕不开的存在。如果你是想快速上手做一款微信小游戏、休闲手游,或者想从零开始接触游戏开发,用它起步会比直接啃Unity或者自研引擎舒服很多。尤其在国内,它的中文文档、社…

2026/9/18 5:26:22

React Native Hermes 引擎配置与性能优化最佳实践

最近整理手头的 React Native 工程时,我把好几个项目里零零散散的 Hermes 配置、白屏排查记录和性能调参笔记归拢成了一个统一的东西。因为核心就是围绕 Hermes 引擎做一套“开箱即用”的配置与最佳实践集,我给它起名叫 oh-my-hermes——灵感来自 oh-my-…

2026/9/18 6:26:24

配置失败本质与排查指南:从JDK环境变量到AI本地模型保存

"为什么一直配置失败呢??"这句话我在工位上听了快十年。新来的实习生、转岗的测试、甚至一些干了三五年的后端,都曾在某个深夜对着黑底白字的命令行问出同一个问题。最近这两周,"jdk环境变量配置失败"和"…

2026/9/18 6:26:24

OpenAI Agents SDK Python:构建高效多智能体工作流

1. 项目背景与核心价值OpenAI Agents SDK Python 是一个专为构建多智能体工作流设计的轻量级框架。作为一名长期从事AI应用开发的工程师,我最初接触这个项目时就被它的设计理念所吸引——它完美解决了我们在实际业务中遇到的三个痛点:多Agent协作的复杂性…

2026/9/18 6:26:24

MiroFish:基于 SQLite 与加权评分模型的野钓记录决策工具

三个多月前,我把一套自己断断续续写了半年的钓鱼记录工具正式命名为 MiroFish,名字取的是 mirror(镜像)加上 fish(鱼)——用你自己的历史渔获数据,去镜像出下一次出钓的最优解。它不是什么大厂产…

2026/9/18 6:26:24

零售业数字化转型:数据中台与全渠道融合实践

1. 零售业数字化转型的必然趋势百货零售行业正面临前所未有的挑战与机遇。根据我过去五年参与零售数字化项目的经验,传统百货商场客流年均下降8-12%,而数字化成熟度高的企业却能实现15%以上的线上销售增长。这份85页的德勤解决方案PPT,恰好系…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/18 0:01:09

Google Colab 实战:运行模型、数据加载与报错排查

1. 为什么我劝你先搞懂 Colab 的运行模型1.1 Colab 到底是什么,跟本地跑代码差在哪Google Colab 简单说就是一台跑在浏览器里的 Linux 虚拟机,你打开一个 Notebook,背后就连上了一台带 GPU 的远程机器。你在单元格里敲的每一行 Python&#x…

2026/9/18 0:01:09

C语言数据类型与表达式详解

1. C语言数据与数据类型概述在C语言编程中,数据是程序处理的核心对象。理解数据的分类和特性是掌握C语言的基础。C语言中的数据主要分为四大类:常量、变量、表达式和函数。这些数据类型构成了C语言程序的基本元素,每种类型都有其独特的特性和…

2026/9/18 0:01:09

SQL时间字段指定时间段查询:区间语义、索引与时区避坑

上周排查一个线上问题&#xff0c;用户反馈"昨天的订单一条都没查到"&#xff0c;但数据库里明明躺着两千多条。最后定位下来&#xff0c;不是数据丢了&#xff0c;也不是接口挂了&#xff0c;而是那个查询条件把时间段写成了> 2024-05-20 00:00:00 AND < 2024…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码