#[non_exhaustive]pub struct ForEach {
pub over: Value,
pub item: String,
pub max_concurrency: usize,
pub collect: Option<String>,
pub into: Option<String>,
/* private fields */
}Expand description
Run a task’s function once per element of an array.
{ "id": "infer", "name": "One move per participant",
"for_each": { "over": { "var": "data.participants" }, "as": "p",
"max_concurrency": 8,
"collect": "temp_data.move", "into": "temp_data.moves" },
"continue_on_error": true,
"function": { "name": "model_infer", "input": {
"model": { "var": "temp_data.p.model" },
"output": "temp_data.move" } } }The task’s condition is evaluated once. over is then
evaluated once, and the function runs once per element with the element at
temp_data.<as> and its index at temp_data.<as>_index.
§Every call is isolated
Each call runs against its own copy of the message, taken before the
first call, so no call sees another’s writes — not even when they run one
at a time. When the calls finish, each is folded back into the message in
element order: the errors it recorded, then its writes, then its audit
entry. That is what makes max_concurrency a
timing knob only: the message comes out the same at every setting.
The fold replays the writes a handler made through TaskContext::set.
A write made by reaching into ctx.message_mut() directly is not carried
back. The bindings themselves live only in each call’s copy, so neither
temp_data.<as> nor temp_data.<as>_index is left behind — unless a
handler writes under one of them itself, which is replayed like any other
write. collect may not name a path under either.
§Results
collect names the slot each call writes its result to,
and into the array the results land in, in element order.
An element that failed (a returned Err or a status of 400 or more), or
never ran, or wrote nothing at collect, leaves null at its index.
§Failures
The task’s continue_on_error applies per element. A call that fails the
task — an Err or a 5xx without continue_on_error — or returns
TaskOutcome::Halt stops new calls from starting; calls already running
finish. Folding stops at the first element whose record fails the task or
halts, so the elements after it contribute nothing — however many had
already finished. terminal and halt_on apply once, after the whole
fan-out.
Only handler-backed functions may fan out (http_call, enrich,
publish_kafka, custom handlers): built-ins run inline, and JSONLogic’s
map, filter and reduce already cover per-element transforms.
#[non_exhaustive]: the compiled fields are engine internals. Parse a
workflow from JSON to get one.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.over: ValueJSONLogic evaluated once, after the task’s condition. It must yield an
array; anything else, null included, fails the task. An empty array
runs no call.
item: Stringtemp_data field holding the element in each call’s view. The JSON
key is as. The element’s index is at <as>_index beside it.
max_concurrency: usizeCalls in flight at once. 1, the default, runs them one at a time.
collect: Option<String>Context path each call writes its result to, such as
temp_data.move. Requires into. Must not overlap
temp_data.<as> or temp_data.<as>_index.
into: Option<String>Context path that receives the results as an array in element order.
Requires collect.