ForEach 控制节点
概述
ForEach控制节点用于循环处理数组中的每个元素,为每个元素创建独立的子工作流环境进行处理。作为纯控制节点,它专注于循环控制逻辑,不进行结果聚合。
节点配置
基本属性
{
"id": "foreach-1",
"type": "control",
"config": {
"identifier": "foreach",
"title": "数组循环处理",
"description": "逐项处理数组数据"
}
}
输入参数
| 参数名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
data | Object/Array | - | 要处理的数据,可以是数组或包含数组的对象 |
iterableField | String | 'list' | 从data对象中提取数组的字段名 |
maxIterations | Number | 1000 | 最大迭代次数限制,防止无限循环 |
continueOnError | Boolean | true | 单项失败时是否继续处理其他项 |
输出端口
| 端口名 | 描述 |
|---|---|
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"
数据获取逻辑:
- 如果
data是数组 → 直接迭代 - 如果
data是对象 → 提取iterableField字段(默认为list) - 其他情况 → 使用空数组
迭代上下文
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作为纯控制节点,不返回聚合结果。所有的业务逻辑和数据聚合都应该在子工作流中的节点内完成!