关于 n8n-loops
n8n-loops 是面向 n8n 工作流构建者的循环与迭代指南,说明默认逐项执行、executeOnce 和 Loop Over Items 的区别。它还介绍分页、批处理、限流及轮询等场景的选择方式。
主要功能
- 01区分三种迭代机制
- 02说明 executeOnce 的适用场景
- 03介绍 Loop Over Items 批处理
- 04优先使用 HTTP Request 内置分页
- 05涵盖限流、轮询与分批错误处理
适用场景
- 01逐项处理输入数据
- 02一次汇总或发送通知
- 03按批次调用 API 并限流
- 04获取分页 API 的完整结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ n8n-io/ skills --skill n8n-loops安装后在 Claude Code 中输入/n8n-loops确认已加载。
附带文件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 技能中的 executeOnce 部分。
强烈建议
- 默认迭代足够时,不要构建循环。 大多数节点(HTTP Request、原生服务节点、Set、IF/Switch)会自动对每个输入条目运行一次:输入 N 个条目,运行 N 次。要发出 N 个 HTTP 调用或创建 N 条记录,只需将源节点连接到该节点。除非需要控制每次迭代,否则不要使用
Loop Over Items。(注意:Execute Workflow是个例外。它默认将所有条目作为单个批次处理。n8n-subworkflows中介绍了mode: 'each'。) - 对于分页 API,请使用 HTTP Request 内置的分页功能。 除非 API 确实特殊,否则不要使用
Loop Over Items+ 手动$pageCount重新实现。请参阅references/HTTP_PAGINATION.md。 Loop Over Items用于显式分批或控制每次迭代(速率限制、每批错误恢复、有状态分块、轮询)。请参阅references/LOOP_OVER_ITEMS.md。- 逐条目迭代是顺序执行,而非并行执行。 即使分支看起来是并行的,每个条目也会在下一个条目开始前处理完成。如需真正的并发模式,请参阅
n8n-subworkflows。
决策树:我需要哪种机制?
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。)
不应使用的场景:
- 逐条目操作。 默认情况下,每个条目发送一条通知,通常是正确做法。
- 针对每个条目分发的 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 references/MERGE_NODE.md。
只想要一个摘要,LLM 调用却触发 N 次
这和聚合用的 Code 节点情况相同。LLM 节点在提示词中使用 $input.all() 时,如果没有设置 executeOnce: true,就会进行 N 次相同的调用,消耗 N 倍 Token,并返回 N 个相同的答案。
何时使用 Loop Over Items
默认迭代已能处理大多数情况。以下情况可使用 Loop Over Items:
- 速率限制。“每次处理 10 项,批次之间等待 1 秒。”
- 请求体为数组的批量 API 调用。“将 50 项分块发送到 /bulk。”
- 按批次处理错误。“如果某个批次失败,就记录错误并继续。”
- 有状态迭代。“每次迭代都依赖上一次的输出。”
- 轮询长时间运行的任务。“启动任务,每 30 秒检查一次状态,直到完成或达到上限。”使用
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。
场景 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 mode: 'each' |