关于 n8n-loops-official
面向 n8n 工作流构建者的技能,解释节点逐项运行、executeOnce 和 Loop Over Items 的适用场景。还介绍批处理、分页 API 及避免不必要循环的判断方法。
主要功能
- 01说明节点默认逐项迭代
- 02讲解 executeOnce 设置
- 03介绍 Loop Over Items 批处理
- 04使用 HTTP Request 内置分页
- 05涵盖轮询与有状态迭代
适用场景
- 01按批次处理请求以控制速率
- 02将记录分块发送到批量 API
- 03轮询长时间运行的任务
- 04避免聚合节点重复执行
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ n8n-io/ skills --skill n8n-loops-official安装后在 Claude Code 中输入/n8n-loops-official确认已加载。
附带文件references/HTTP_PAGINATION.mdreferences/LOOP_OVER_ITEMS.md
详细介绍
n8n 循环
“循环”有三种含义,分别对应三种机制:
- “对每个项目运行此节点。” 默认行为。大多数节点会自动循环,因此无需操作。
- “只用所有项目运行此节点 一次,而不是对每个项目运行一次。”
executeOnce设置(单个布尔值)。 - “按明确的批次处理项目,并在迭代之间控制流程。”
Loop Over Items节点(以前称为Split In Batches)。
对于分页 API,HTTP Request 节点内置了分页功能。它几乎总是比手动构建页数循环更可取。
模型:项目是一个数组
数据以项目数组的形式在节点之间流动,每个项目的格式为 { json: {...}, binary?: {...} }。50 个项目 = 一个包含 50 个条目的数组。
默认情况下,节点对每个项目运行一次。一个接收 50 个输入项目的 HTTP Request 会发送 50 个请求,并输出 50 个结果项目。这种隐式循环是大多数工作流所依赖的机制。
输入数组就是循环。 通过控制数组来控制迭代。
不容妥协的规则
只要节点应该在每次运行中触发一次,而不是对每个项目触发一次,就设置 executeOnce: true。 这包括通过 $input.all() / $('Node').all() 以及 .map() / .filter() / .reduce() 对整个数据集进行聚合的任何表达式(否则,若上游有 N 个项目,聚合就会计算 N 次,通常会导致下游出现 N 个重复项)。也包括单次通知、聚合写入和摘要消息。
反例: .all() 与另一个节点的 .item 搭配使用(逐项目查找,例如 $('Get Tags').all().filter(tag => $('Search Posts').item.json.tag_ids.includes(tag.json.id)))属于真正的逐项目操作,应保持 executeOnce 关闭。完整区别请参阅 n8n-expressions-official 技能中的 executeOnce 部分。
强烈建议的默认做法
- 默认迭代足够时,不要自行构建循环。 大多数节点(HTTP Request、原生服务节点、Set、IF/Switch)都会自动对每个输入项目运行一次:输入 N 个项目,运行 N 次。若要发起 N 个 HTTP 请求或创建 N 条记录,只需将来源连接到该节点。除非需要控制每次迭代,否则不要使用
Loop Over Items。(注意:Execute Workflow是个例外。它默认使用包含所有项目的单个批次。有关mode: 'each',请参阅n8n-subworkflows-official。) - 对于分页 API,使用 HTTP Request 的内置分页功能。 除非 API 确实很特殊,否则不要用
Loop Over Items+ 手动$pageCount重新实现。请参阅references/HTTP_PAGINATION.md。 Loop Over Items用于显式分批或控制每次迭代(速率限制、逐批错误恢复、有状态分块、轮询)。请参阅references/LOOP_OVER_ITEMS.md。- 逐项目迭代是顺序执行,而非并行执行。 即使看起来是并行的分支,每个项目也会在下一个项目开始之前完成。有关真正的并发模式,请参阅
n8n-subworkflows-official。
决策树:我需要哪种机制?
Need to do something for each item?
├── Default per-item iteration is enough
│ └── Just connect the node. Done.
│
├── The node should run once total, not once per item?
│ └── Set executeOnce: true on the node
│
├── Paginated API (multiple HTTP calls to fetch all pages)?
│ └── Use HTTP Request's Pagination option (see references/HTTP_PAGINATION.md)
│
├── Need explicit batching (rate limit, chunk size, per-batch error handling)?
│ └── Use Loop Over Items node (see references/LOOP_OVER_ITEMS.md)
│
└── Need to recurse / repeat with state until a condition is met?
└── Loop Over Items with Reset, OR a sub-workflow that calls itself.
Both are advanced patterns; see references/LOOP_OVER_ITEMS.md.
executeOnce:单次触发设置
每个节点的 Settings 选项卡中都有 Execute Once 开关。开启后,节点只使用第一个输入项目运行一次。在 SDK 代码中,按如下所示设置 executeOnce: true;对于现有工作流,可通过 update_workflow 的 setNodeSettings 应用(n8n 2.24.0+)。
{
name: 'Aggregate Slack',
type: 'n8n-nodes-base.slack',
parameters: { /* ... */ },
executeOnce: true,
}
适用场景:
- 通知和聚合写入。 收到 100 个项目时,“摘要消息”不应触发 100 次。
- 计数器、总数、报告。 任何计算
$input.all().length等内容的操作都应只运行一次。 - 通过
$input.all()/$('Node').all()跨整个数组进行聚合的表达式。 否则,聚合会针对每个上游项目运行。(使用.all()并通过另一个节点的.item进行筛选的逐项目查找属于反例,参见n8n-expressions-official。)
不适用场景:
- 逐项目操作。 默认情况下,对每个项目分别发送一次通知通常是正确做法。
- 按项目展开的 HTTP 请求。 你需要对每个项目分别发起一次调用。
最常见的错误:忘记在聚合节点上设置 executeOnce,导致扇出后同一条消息触发 50 次。
隐式循环带来的问题
默认的逐项迭代很实用,但也有不适用的时候。常见的意外情况:
一个“单次”配置节点会对每个项目触发一次
扇出后的 Set 节点会对每个项目运行一次,从而生成 N 份相同的常量。通常没问题,但开销较大的表达式(较长的 JSON.stringify、Luxon 解析)会运行 N 次。将 Set 移到扇出之前,或设置 executeOnce: true。
聚合用的 Code 节点会运行 N 次
如果 Code 节点读取 $input.all() 来计算总和,但没有设置 executeOnce: true,它就会对每个上游项目运行一次,生成 N 个相同的项目。设置 executeOnce: true。
Webhook 响应节点会触发两次
这是最麻烦的情况。Respond-to-Webhook 会对每个输入项目触发一次;两个分支未合并就汇合时,它会触发两次:第一个响应生效,其余响应会记录错误。先合并,或确保只有一个分支到达响应节点。参见 n8n-node-configuration-official references/MERGE_NODE.md。
想要一次摘要,LLM 调用却触发了 N 次
这和聚合用的 Code 节点情况相同。若 LLM 节点的提示词中使用了 $input.all(),但没有设置 executeOnce: true,就会进行 N 次相同的调用,消耗 N 倍 Token,并返回 N 个相同的答案。
何时使用 Loop Over Items
默认迭代已能处理大多数情况。以下场景可使用 Loop Over Items:
- 限流。“每次处理 10 个批次,批次之间等待 1s。”
- 使用数组请求体的批量 API 调用。“将每批 50 个项目发送到 /bulk。”
- 按批次处理错误。“如果某个批次失败,就记录错误并继续。”
- 有状态迭代。“每次迭代都依赖上一次的输出。”
- 轮询长时间运行的任务。“启动任务,每隔 30s 检查状态,直到完成或达到上限。”使用
reset: true,并设置$runIndex上限。 - 逐项执行多分支处理并聚合。“对每个输入并行运行转换,合并后再聚合。”完成输出(索引 0)会携带结果。
关于连线详情、输出索引易错点(输出 0 = 完成,输出 1 = 循环)和示例,请参见 references/LOOP_OVER_ITEMS.md。
何时不应使用 Loop Over Items
最常见的自我说服理由是:“我需要等所有项目处理完再执行下一步,所以要添加 Loop Over Items 并使用 done 输出。”这并不需要 Loop Over Items。 默认的逐项迭代本来就会等待:每个项目都会先流经完整的下游链路,再开始处理下一个项目;循环后的节点不会“提前”触发。
解决这个问题的方法几乎总是:删除 Loop Over Items 节点,其他什么都不改,然后发布。这就是下面的场景 1,适用于大多数情况。
这四种场景彼此独立。请将它们视为单独的决策,而不是都能“取代” Loop Over Items 的备选方案。大多数工作流适用场景 1,完成即可。只有当目标符合特定条件时,才需要考虑场景 2 和 3。场景 4 则是 Loop Over Items 确实有用的少数情况。
场景 1:默认的逐项迭代(默认且最常见)
源节点发出 N 个项目,逐项处理器运行 N 次,下游链路依次执行。不需要 Loop Over Items、executeOnce 或 Aggregate。 直接连接节点即可。
[Source: 20 items]
→ [Per-item processor] # default iter, runs 20 times
→ [Next step] # runs after each item, in order
如果在这里添加了 Loop Over Items + done,理由是“等待所有项目完成”或“让下一个节点对每个项目运行”,请删除 Loop Over Items 节点,直接将源节点连接到处理器。不要用任何东西替代它。 默认迭代已经能完成这项工作。例外情况是 Execute Workflow(子工作流):它默认采用包含所有项目的单批次,因此要逐项调用,需在该节点设置 mode: 'each',而不是使用 Loop。参见 n8n-subworkflows-official。
场景 2:下游节点总共只应执行一次(executeOnce: true)
与场景 1 无关。仅当有一个特定的下游节点需要每次运行执行一次,而非每个项目执行一次时,才适用:处理完 20 篇论文后发送一封摘要邮件、循环结束后写入一份摘要,或发送一条最终 Slack 通知。
在该节点上设置 executeOnce: true。该节点会接收所有上游项目,但只执行一次。这是节点级设置,不能替代 Loop:场景 1 中的逐项处理器仍会运行 N 次,只有这个下游节点会合并执行。
场景 3:节点需要将项目作为单个数组接收(Aggregate)
与场景 1 无关。仅当下游节点的输入要求是一个列表,而非逐项调用时,才适用(例如,节点需要 JSON 数组作为请求体,或 LLM 提示词需要整个列表)。
使用 Aggregate 节点将逐项数据流合并为一个包含数组的项目。同样,这不能替代 Loop。
场景 4:真正的批处理(Loop Over Items 唯一适用的情况)
速率限制(每次处理 N 个项目,并在批次之间使用 Wait)、分块批量 API 调用(向 /bulk POST 包含 50 个项目的数组)、按批次处理错误、使用 reset: true 和 $runIndex 上限轮询长时间运行的作业,以及每个批次都依赖前一次输出的有状态迭代。
“等待项目处理完成”不属于此类。默认迭代本身就会等待。
快速辨别
如果你使用 Loop 的唯一理由是“等待项目/逐项运行”,那你属于场景 1。删除 Loop。不要用 executeOnce 或 Aggregate 替代。它们解决的是不同问题,只有在各自对应的场景下才应添加。
何时使用 HTTP 分页
三种常见的分页形式:
- 响应中包含下一页 URL。 每个响应都包含一个
next链接。 - 页码/游标参数。 每次调用递增
?page=N或?cursor=...。 - 遇到空页面或特定状态时停止。
HTTP Request 可通过其 Pagination 选项原生处理以上情况:设置模式,提供下一页表达式,节点便会在内部循环,并返回包含所有页面项目的单个输出数组。
除非 API 的行为无法用内置模式表达,否则不要用 Loop Over Items + 手动 $pageCount 重新实现。
参见 references/HTTP_PAGINATION.md。
子工作流递归
对于真正的递归任务(遍历树、带退避的重试、“处理此项及其子项”),自调用子工作流比使用 Reset 的 Loop Over Items 更简洁。输入参数可承载递归状态。
Sub-workflow: Walk Tree
inputs: { node_id, depth }
body: process node, look up children, for each child call self with depth+1
注意递归深度。n8n 有嵌套执行限制。对于深度递归,队列 + 扁平循环优于真正的递归。
参考文件
| 文件 | 适用场景 |
|---|---|
references/LOOP_OVER_ITEMS.md | 配置 Loop Over Items 节点、批处理、速率限制、有状态迭代 |
references/HTTP_PAGINATION.md | 调用分页 API、配置 HTTP Request 分页模式 |
反模式
| 反模式 | 问题所在 | 修复方法 |
|---|---|---|
为了“实现循环”而添加 Loop Over Items,但默认迭代已能满足需求 | 工作流更难阅读,却没有任何收益;循环输出与完成输出也容易接错 | 直接连接节点,让默认迭代处理即可 |
聚合 Code 节点未设置 executeOnce | 同一聚合计算 N 次,输出包含 N 个相同的条目 | 设置 executeOnce: true |
使用 Loop Over Items + $pageCount 手动分页循环 | 重新实现了 HTTP Request 原生支持的功能,停止条件也很脆弱 | 使用 HTTP Request 的 Pagination 选项 |
| 想要摘要,却为每个条目发送一条 Slack 消息 | Slack 频道消息刷屏,触发速率限制,还会让人尴尬 | 在 Slack 节点上设置 executeOnce: true,并在上游构建摘要 |
两个分支都到达 Respond to Webhook | 响应两次,下游调用方会看到错误 | 在响应节点前合并分支,或确保只有一个分支能到达该节点 |
使用 Reset 的 Loop Over Items 没有明确的终止条件 | 陷入无限循环,n8n 会持续占用内存,直到执行被终止 | 始终设置明确的终止条件。对于分页 API,优先使用 HTTP 分页 |
在同一工作流中将一个 Loop Over Items 嵌套在另一个内部 | 运行时会出错,但验证可以通过 | 将内层循环移到子工作流中,并在每次外层迭代时调用。参见 n8n-subworkflows-official mode: 'each' |