跳到主要内容

ForEach 控制节点

概述

ForEach控制节点用于循环处理数组中的每个元素,为每个元素创建独立的子工作流环境进行处理。作为纯控制节点,它专注于循环控制逻辑,不进行结果聚合。

节点配置

基本属性

{
"id": "foreach-1",
"type": "control",
"config": {
"identifier": "foreach",
"title": "数组循环处理",
"description": "逐项处理数组数据"
}
}

输入参数

参数名类型默认值描述
dataObject/Array-要处理的数据,可以是数组或包含数组的对象
iterableFieldString'list'从data对象中提取数组的字段名
maxIterationsNumber1000最大迭代次数限制,防止无限循环
continueOnErrorBooleantrue单项失败时是否继续处理其他项

输出端口

端口名描述
item连接到子工作流,传递当前迭代项

数据输入方式

方式1:直接数组

data本身是数组时,ForEach直接迭代这个数组:

// 数据生成器返回
return [1, 2, 3, 4, 5];

// ForEach会迭代每个数字

方式2:对象中的数组字段

data是对象时,ForEach从中提取指定字段的数组:

// 数据生成器返回
return {
list: [
{ id: 1, name: "商品A" },
{ id: 2, name: "商品B" }
],
total: 2
};

// ForEach会迭代list字段中的数组

方式3:自定义字段名

通过iterableField参数指定字段名:

// 数据生成器返回
return {
products: [
{ id: 1, name: "商品A" },
{ id: 2, name: "商品B" }
]
};

// 设置iterableField为"products"

数据获取逻辑

  1. 如果data是数组 → 直接迭代
  2. 如果data是对象 → 提取iterableField字段(默认为list
  3. 其他情况 → 使用空数组

迭代上下文

ForEach为每次迭代提供丰富的上下文信息,在子工作流中可以通过context[节点ID]访问:

// 上下文结构
context['foreach-1'] = {
item: currentItem, // 当前迭代项
_iteration_info: {
index: 0, // 当前索引(从0开始)
total: 5, // 总项目数
isFirst: true, // 是否第一项
isLast: false // 是否最后一项
}
}

使用示例

示例1:数值数组处理

{
"nodes": {
"data-generator": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return [10, 20, 30, 40, 50];"
}
},
"foreach-numbers": {
"type": "control",
"config": {
"identifier": "foreach"
}
},
"number-processor": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": `
const number = context['foreach-numbers'].item;
const info = context['foreach-numbers']._iteration_info;

console.log(\`处理第\${info.index + 1}个数字: \${number}\`);

return {
original: number,
doubled: number * 2,
isLast: info.isLast
};
`
}
}
},
"edges": [
{
"source": "data-generator",
"target": "foreach-numbers",
"sourcePort": "result",
"targetPort": "data"
},
{
"source": "foreach-numbers",
"target": "number-processor",
"sourcePort": "item",
"targetPort": "parameters"
}
]
}

示例2:对象数组处理

{
"nodes": {
"user-generator": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": `
return {
list: [
{ id: 1, name: "张三", age: 25 },
{ id: 2, name: "李四", age: 30 },
{ id: 3, name: "王五", age: 28 }
]
};
`
}
},
"foreach-users": {
"type": "control",
"config": {
"identifier": "foreach"
}
},
"user-processor": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": `
const user = context['foreach-users'].item;
const info = context['foreach-users']._iteration_info;

return {
...user,
processOrder: info.index + 1,
isAdult: user.age >= 18,
isFirstUser: info.isFirst,
isLastUser: info.isLast
};
`
}
}
},
"edges": [
{
"source": "user-generator",
"target": "foreach-users",
"sourcePort": "result",
"targetPort": "data"
},
{
"source": "foreach-users",
"target": "user-processor",
"sourcePort": "item",
"targetPort": "parameters"
}
]
}

示例3:带聚合处理

{
"nodes": {
"score-generator": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return [85, 92, 78, 96, 88];"
}
},
"foreach-scores": {
"type": "control",
"config": {
"identifier": "foreach"
}
},
"score-processor": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": `
const score = context['foreach-scores'].item;
const info = context['foreach-scores']._iteration_info;

// 初始化累加器
if (!context._scoreStats) {
context._scoreStats = {
total: 0,
count: 0,
scores: []
};
}

// 累加统计
context._scoreStats.total += score;
context._scoreStats.count++;
context._scoreStats.scores.push(score);

// 如果是最后一项,计算平均分
if (info.isLast) {
const average = context._scoreStats.total / context._scoreStats.count;
return {
average: Math.round(average * 100) / 100,
total: context._scoreStats.total,
count: context._scoreStats.count,
allScores: context._scoreStats.scores
};
}

return { currentScore: score, processed: info.index + 1 };
`
}
}
},
"edges": [
{
"source": "score-generator",
"target": "foreach-scores",
"sourcePort": "result",
"targetPort": "data"
},
{
"source": "foreach-scores",
"target": "score-processor",
"sourcePort": "item",
"targetPort": "parameters"
}
]
}

错误处理

默认行为(continueOnError = true)

// 单项失败时继续处理其他项
const continueOnError = inputs.continueOnError !== false; // 默认为true

if (continueOnError) {
console.warn('迭代失败,继续下一个迭代');
results.push(null); // 失败的迭代用null表示
}

严格模式(continueOnError = false)

// 任何失败都停止整个循环
if (!continueOnError) {
throw error; // 抛出异常,停止执行
}

执行特性

顺序执行

ForEach采用顺序执行模式,逐个处理数组元素:

  • 等待当前迭代完成后才开始下一个
  • 避免并发资源竞争
  • 保证处理顺序

执行指标

ForEach会输出详细的执行统计信息:

// 控制台输出示例
ForEach节点执行完成 {
nodeId: 'foreach-scores',
totalIterations: 5,
successCount: 5,
errorCount: 0,
executionTime: '125ms'
}

安全限制

  • 最大迭代次数:默认1000次,防止无限循环
  • 数据转换:自动处理非数组数据
    • null/undefined → 空数组[]
    • 对象 → Object.values(object)
    • 其他类型 → [value]

常用模式

模式1:累加计算

// 在子工作流中实现累加
const accumulator = `
const item = context['foreach-1'].item;
const info = context['foreach-1']._iteration_info;

if (!context._sum) context._sum = 0;
context._sum += item;

if (info.isLast) {
return { totalSum: context._sum };
}
return { currentSum: context._sum };
`;

模式2:条件过滤

// 条件处理
const filter = `
const item = context['foreach-1'].item;
const info = context['foreach-1']._iteration_info;

if (!context._results) context._results = [];

if (item > 50) { // 条件判断
context._results.push(item);
}

if (info.isLast) {
return { filteredResults: context._results };
}
return null;
`;

模式3:批次处理

// 分批处理
const batchProcessor = `
const item = context['foreach-1'].item;
const info = context['foreach-1']._iteration_info;
const batchSize = 3;

if (!context._batch) context._batch = [];
context._batch.push(item);

// 达到批次大小或最后一项时输出
if (context._batch.length === batchSize || info.isLast) {
const batch = [...context._batch];
context._batch = []; // 重置
return { batch: batch, batchNumber: Math.ceil((info.index + 1) / batchSize) };
}

return null;
`;

最佳实践

1. 数据输入建议

// ✅ 推荐:直接数组(最简单)
return [1, 2, 3, 4, 5];

// ✅ 推荐:结构化对象(更灵活)
return {
list: [1, 2, 3, 4, 5],
metadata: { source: 'api', timestamp: Date.now() }
};

2. 上下文使用技巧

// 利用迭代信息
const processor = `
const item = context['foreach-1'].item;
const info = context['foreach-1']._iteration_info;

// 第一项初始化
if (info.isFirst) {
context._initialized = true;
}

// 最后一项清理
if (info.isLast) {
return { completed: true, processed: info.total };
}

return { item: item, position: info.index + 1 };
`;

3. 错误处理策略

// 根据需求选择错误处理模式
{
"continueOnError": true // 容错模式,适合数据清洗
}

{
"continueOnError": false // 严格模式,适合关键业务
}

重要提醒:ForEach作为纯控制节点,不返回聚合结果。所有的业务逻辑和数据聚合都应该在子工作流中的节点内完成!