mcpField
N
n8n-loops

n8n-loops

github.com/n8n-io开发558341 次安装2 周前更新
代码仓库

关于 n8n-loops

n8n-loops 是面向 n8n 工作流构建者的循环与迭代指南,说明默认逐项执行、executeOnce 和 Loop Over Items 的区别。它还介绍分页、批处理、限流及轮询等场景的选择方式。

主要功能

  1. 01区分三种迭代机制
  2. 02说明 executeOnce 的适用场景
  3. 03介绍 Loop Over Items 批处理
  4. 04优先使用 HTTP Request 内置分页
  5. 05涵盖限流、轮询与分批错误处理

适用场景

  1. 01逐项处理输入数据
  2. 02一次汇总或发送通知
  3. 03按批次调用 API 并限流
  4. 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 循环

“循环”有三种含义,分别对应三种机制:

  1. “对每个条目运行此节点。” 默认行为。大多数节点会自动循环,因此无需操作。
  2. “使用所有条目运行此节点一次,而不是对每个条目运行一次。” executeOnce 设置(单个布尔值)。
  3. “通过控制流,在明确分批的条目上进行处理。” 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'

评论

支持 Markdown