从测试快照看 eslint-plugin-unicorn 的 no-async-iterator-callback 规则:为何同步迭代器辅助方法不能接收异步回调 📅 发布时间:2026/9/19 11:49:49 👁 浏览次数: 从测试快照看 eslint-plugin-unicorn 的 no-async-iterator-callback 规则为何同步迭代器辅助方法不能接收异步回调【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicornno-async-iterator-callback是 eslint-plugin-unicorn 中用于拦截“把异步回调传给同步迭代器辅助方法”的静态分析规则。本文以仓库内的 AVA 测试快照 test/snapshots/no-async-iterator-callback.js.md 为线索结合规则文档 docs/rules/no-async-iterator-callback.md 与实现源码 rules/no-async-iterator-callback.js系统梳理该规则的报错语义、可检测的写法矩阵、TypeScript 类型信息增强能力与检测边界帮助读者在真实项目中正确使用并理解这条规则。快照文档是什么规则行为的“契约级”见证快照文件test/snapshots/no-async-iterator-callback.js.md由 AVA 测试框架自动生成与其对应的二进制快照test/snapshots/no-async-iterator-callback.js.snap成对存在。它记录的并不是源码或配置而是运行 test/no-async-iterator-callback.js 中所有invalid用例后逐条精确匹配到的 ESLint 报错输出——包括输入源码、报错行列位置^指示符和完整消息文本。因此这份快照相当于规则的“行为契约”每一条invalid(N)都对应一种真实代码形态而每条报错都携带相同的核心消息模板Do not pass an asynchronous callback to Iterator#{{method}}(); returned promises are not awaited.消息中的{{method}}由规则实现中的messages定义注入源码见 rules/no-async-iterator-callback.js。快照中 100 余条用例全部命中同一模板证明该规则只有一条报错消息、不区分错误等级。规则动机同步迭代器辅助方法不会等待 Promise为什么这条规则被设计为type: problem问题级规则文档给出了底层语义依据同步迭代器辅助方法Iterator Helper根本不会 await 回调返回的 Promisefilter、some、every、find把返回的 Promise 一律当作真值处理与 Promise 最终 resolve 的结果无关forEach直接丢弃返回的 Promise异步回调可能尚未执行完就继续迭代flatMap要求回调返回同步的 iterable/iterator返回 Promise 会在消费时直接抛出TypeError。关键点在于即使在外层对辅助方法调用加await也无法让内部回调被 await。这正是该规则存在价值的核心论证也是快照中invalid(42)await Iterator.from(values).forEach(async value value)依然报错的原因——await只能等待forEach返回的同步结果而无法等待回调内部的异步副作用。规则覆盖面六个被禁止的方法规则实现中用一个Set精确锁定被检查的方法名rules/no-async-iterator-callback.jsconst methods new Set([filter, forEach, some, every, find, flatMap]);快照中对这六个方法逐一验证了“箭头函数”与“async function 表达式”两种回调形态invalid(1)invalid(12)。与之相对map和reduce是被明确放行的map产生 Promise 数组后配合Promise.all消费、reduce累积 Promise 都属于合理的异步模式参见规则文档与测试中的 valid 用例Iterator.from(values).map(async value value)、Iterator.from(values).reduce(...)。这是理解该规则“为什么只禁止这六个方法”的关键设计取舍。接收者receiver识别什么对象会被判定为迭代器快照中大量用例用于验证接收者识别逻辑其底层实现在 rules/shared/iterator-helpers.js 的isIteratorExpression中。从快照可以归纳出以下被认可的“同步迭代器来源”接收者形态快照用例识别依据Iterator.from(values)invalid(1)等全局Iterator.from静态方法globalThis.Iterator.from(values)invalid(13)globalThis上的全局IteratorIterator.concat(a, b)/zip/zipKeyedinvalid(14)invalid(16)其余三个全局静态方法array.values()/map.keys()/set.entries()invalid(17)invalid(19)集合迭代方法string.matchAll(pattern)invalid(20)matchAll返回RegExpStringIterator辅助方法链.map(...).take(2).filter(...)invalid(21)惰性辅助方法链式追踪同步生成器调用generate()invalid(25)本地同步生成器函数调用const绑定及别名追踪invalid(22)invalid(24)不可变绑定追踪其中invalid(22)const iterator ...; iterator.filter(...)与invalid(23)再经一次别名赋值证明规则具备跨语句追踪不可变绑定的能力。实现上getImmutableValue只跟随“无类型注解的 const 绑定”与“未被重新赋值的函数声明”刻意不推断属性、解构、可变绑定与函数返回值——这一保守策略可以在测试 valid 用例中找到对应证据如const {iterator} Iterator.from(values); ...不解构、let iterator ...不追踪。回调形态检测语法级三种识别路径对于没有类型信息的纯 JS 场景规则通过isAsyncCallbackrules/no-async-iterator-callback.js做语法级识别快照逐条验证了内联异步函数async value value、async function (value) {...}覆盖invalid(1)invalid(12)直接 const 绑定到异步函数const callback async value value; Iterator.from(values).filter(callback)对应invalid(26)未重新赋值的本地异步函数声明async function callback(value) { return value; } ...every(callback)对应invalid(28)且invalid(46)证明函数声明位于调用之后函数提升场景也能被识别。同时规则刻意忽略异步生成器回调async function * () {}、被重新赋值的绑定、跨作用域参数、以及import引入的异步函数类型信息缺失时无法跨模块分析——这些都能在测试的 valid 用例中找到对应项。语法变体覆盖可选链、计算属性、包装表达式、JSX快照的一大价值在于展示规则对“等价写法”的覆盖密度这是衡量规则健壮性的直接证据可选链各位置Iterator.from(values)?.filter(...)、Iterator.from(values).filter?.(...)、Iterator?.from(values)...、Iterator.from?.(values)...、map.values?.().filter(...)invalid(29)invalid(33)计算属性调用filter、filter乃至const method filter; ... methodinvalid(36)invalid(38)括号包裹与注释(Iterator.from(values)).filter((async value value))、回调周围夹杂注释invalid(39)、invalid(40)嵌套在表达式中的调用作为函数实参consume(...toArray())、被await、带额外参数/展开参数invalid(41)invalid(44)JSX 表达式内div{Iterator.from(values).some(async value value)}/divinvalid(45)。这些用例共同说明规则在入口处通过unwrapExpression统一解包ChainExpression、ParenthesizedExpression等包装节点再执行成员表达式与方法名解析因此上述形态都能稳定命中。TypeScript 语法级测试类型注解、断言与泛型实例化快照中段invalid(1)invalid(18)使用 TypeScript 解析器但无类型信息验证了规则对 TS 语法形态的兼容参数类型注解function foo(iterator: Iteratornumber) {...}、IteratorObjectnumber、IterableIteratornumber、Generatornumber等类型名均被识别依据iteratorTypeNames集合类型断言包装(async value value) as Predicate、Predicate(...)、(...)!非空断言、satisfies Predicate表达式invalid(9)invalid(12)泛型回调调用callbacknumber与iterator!.filternumber(...)的TSInstantiationExpression解包invalid(14)、invalid(15)、invalid(17)as断言在接收者一侧(iterator as IteratorObjectnumber)、IteratorObjectnumberiterator以及satisfies Iterablenumberinvalid(7)、invalid(8)、invalid(18)。值得注意的边界当IteratorT被用户自定义类型遮蔽type IteratorT T[]时规则会从当前作用域链中剔除被遮蔽的类型名见isKnownIteratorTypeExpression中的作用域扫描对应测试 valid 用例type IteratorT T[]; function foo(iterator: Iteratornumber) { iterator.filter(async value value); }不报错。类型信息增强Promise 返回类型的深层判定快照第三部分invalid(1)invalid(22)文件名为file.ts并启用projectService类型服务展示的是规则最强大的模式当 TypeScript 类型信息可用时即使回调本身不是 async 函数只要其返回类型包含Promise/PromiseLike就会被标记。这是hasPromiseReturnTyperules/no-async-iterator-callback.js的作用。典型被拦截的形态Iterator.from([1]).filter(() Promise.resolve(false))—— 普通同步箭头函数但返回 Promise六方法逐一验证类型断言后返回 Promisecallback as () Promiseboolean、() PromiseLikebooleancallback联合类型中含 Promise() boolean | Promiseboolean、() Promiseboolean | undefined、T extends boolean | PromiseLikeboolean(callback: () T)对象方法与导入函数object.callback方法返回 Promise、import spawn from nano-spawn后作为回调传入——这两类是纯语法分析无法覆盖、必须依赖类型信息的典型场景函数重载多个declare function callback重载中任一返回 Promise 即报错invalid(22)非空断言回调解包callback!。对应的 valid 用例同样丰富as () boolean、() unknown、any、() boolean | undefined、T extends boolean等不含 Promise 的类型均放行。这证实规则通过getCallSignatures()提取签名返回类型、getBaseConstraintOfType解约束并展开联合类型、再以isPromiseType判定——其判定粒度精确到“返回类型联合中的任一成员”。检测边界规则刻意不做什么综合快照中的 valid 用例与文档声明可以归纳该规则的边界设计数组与未知对象不误报array.filter(async ...)、new Set().forEach(...)、new Uint8Array().filter(...)、object.filter(...)普通对象自定义方法一律放行接收者识别是 best-effort 的map/reduce永远放行即使传 async 回调也是合法模式异步生成器回调放行async function * () {}与async function * values()的调用结果均不报错不可靠的绑定不追踪解构出的变量、let绑定、被重新赋值的函数声明、函数参数、跨文件import的异步函数无类型信息时都不作为判定依据filter()无参、filter(undefined)、filter(...callbacks)放行入口处直接忽略无参数与展开参数调用callback.type SpreadElement直接 return。这些边界保证规则只在高置信度场景报错避免在无法确证迭代器身份时产生噪音。规则元信息与工程实践从规则配置rules/no-async-iterator-callback.js可知type: problem——属于需要修复的错误级规则recommended: unopinionated——在recommended与unopinionated配置中默认启用见规则文档头部说明schema: []——无任何可配置选项属于零配置规则当前languages仅声明js/js但通过快照可以看出测试中使用typescriptEslintParser与projectService对 TS 文件同样生效规则不提供 fixer 与 suggestion因为“改成串行还是并发执行”取决于业务语义无法自动安全修复。快照中invalid(42)的await Iterator.from(values).forEach(...)之所以仍被标记再次呼应规则文档的结论await外层调用并不能让回调被等待唯一正确的做法是把异步逻辑迁移到for...of循环内逐个await规则文档的 ✅ 示例或改用mapPromise.all的并发模式。如何在本地复现与验证快照由 AVA 自动维护读者可以自行复现规则的全部行为# 运行该规则的快照测试自动比对 .snap 与 .md 快照 npx ava test/no-async-iterator-callback.js # 当规则实现或测试用例发生变化时更新快照 npx ava test/no-async-iterator-callback.js --update-snapshots执行后test/snapshots/no-async-iterator-callback.js.md与test/snapshots/no-async-iterator-callback.js.snap会被重新生成两者内容必须保持一致——这保证了“文档化的行为”与“规则实际输出”永不脱节。对于需要类型信息的用例测试通过projectService: {allowDefaultProject: [*.ts]}在测试进程内启动 TypeScript 类型服务见 test/no-async-iterator-callback.js这也提醒使用者在真实项目中若想获得import函数、对象方法、重载等深层 Promise 判定能力需要为 ESLint 配置 TypeScript 类型信息typed linting仅靠语法分析时规则退化为“只识别内联 async 函数、const 直绑与本地函数声明”的保守模式且在类型信息不完整的项目中hasPromiseReturnType会通过 try/catch 静默降级源码注释明确说明保留语法级检测兜底。总结快照文件test/snapshots/no-async-iterator-callback.js.md是理解no-async-iterator-callback规则行为的最佳索引它以可执行、可复现的方式记录了规则对六种迭代器辅助方法、三大类接收者来源、四种回调识别路径、数十种语法变体及类型信息增强场景的完整判定结果。配合 rules/no-async-iterator-callback.js 的实现与 docs/rules/no-async-iterator-callback.md 的语义说明开发者既能快速自查代码中潜在的“异步回调被静默吞掉”问题也能理解该规则“高置信度、零配置、不自动修复”的设计哲学——其背后的工程价值在于在静态层面提前拦截一种运行时几乎无法察觉的异步时序 Bug。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考