Loops
Repeat a part of a flow for every item of a list: the Each component with Sequential turned on and Continue Each at the end of the loop.
Introduction
A loop runs the same steps for every item of a list: every contact from a search, every row of a spreadsheet, every file in a folder. In Appmixer a loop is built from components of the Controls utility module.
The recommended way to build a loop is a sequential loop:
Each (Sequential) ──item──▶ step ▶ step ▶ Continue Each
│
└──done──▶ steps after the loop (use the collected Result)Each with Sequential turned on takes a list and sends one item at a time.
The steps connected to the item port of Each are the loop body. They run once for every item.
Continue Each at the end of the loop body confirms that the item is done, so Each sends the next one. It can also add values from the item to the Result of the loop.
Once all items are done, Each sends done with the collected Result, one entry per item, in the order of the list.
The items are processed one at a time and in their order, every step in the loop body can use the variables of the components before Each, and you get the results of the whole loop without Storage or Join Each.

Other options (advanced)
When the order does not matter and speed does, Each can also run the items differently:
Parallel loop with Join Each (Sequential off, the default of the component): all items are sent at once and processed in parallel, so they can finish in any order. Join Each collects them.
Delayed loop: the items are sent one after another with a pause between them, for example to stay under an API rate limit.
The Each component
Set Field to a variable that holds a list, for example the result of a Find or List component. The list can also be a string with a JSON array. Every message on the item port contains:
Index: the position of the item in the list, starting at 0.
Value: the item itself. When the list has a known schema, the Designer also offers the item's properties as separate variables.
Items Count: the number of items in the list.
Correlation ID: the ID of this run of the loop (see below).
Once all items are done, Each sends one message on the done port with Items Count, Correlation ID and Result. An empty list sends no item, only done with a count of 0.
Without Sequential, done means "all items have been sent", not "all items have been processed". The loop body may still be working on them, and Result is empty. Use a sequential loop when the next step has to wait for the whole loop.
How the Correlation ID connects the loop
A flow can run the same loop several times at once. A trigger may fire twice in a second, or an Each may be nested inside another Each. All these runs send their items through the same components, so the messages of different runs are mixed together on the way. The Correlation ID is what keeps them apart.
Each creates one Correlation ID per run. Every time a list arrives on the in port, Each generates a new Correlation ID and puts it in every item message and in the done message of that run. Two runs never share it. The ID is stable, though: if the engine delivers the same input message again after an error, the loop resumes with the same Correlation ID.
Continue Each finds the waiting run by the Correlation ID. In a sequential loop, map the Correlation ID of the Each item to Continue Each. It tells Continue Each which run of which loop to continue, see The Correlation ID in a sequential loop.
Nested loops. When a loop body contains another Each, every run of the inner Each gets its own Correlation ID. Pair every Continue Each (or Join Each) with the Each on the same level: the inner one uses the Correlation ID of the inner Each, the outer one the Correlation ID of the outer Each.
Parallel loop with Join Each
With Sequential off, Each sends all items at once. To wait until the loop body has processed all of them and get them as one list, add Join Each. It has two input ports:
in: connect the last step of the loop body. Every message that arrives here is one processed item.
count: connect the done port of Each and map its Items Count to Number of input messages. This tells Join Each how many messages to wait for.
When the given number of messages has arrived, Join Each sends them all as Array of items on its out port.


If some items never arrive (a step failed, or a Condition in the loop body filtered them out), Join Each would wait forever. That is why Timeout is on by default: after Timeout (seconds) (300 by default) Join Each sends what has arrived to its timeout port, together with the expected count and the number of items that actually arrived.
Join Each groups messages by the Correlation ID. It keeps a separate counter for every Correlation ID it sees. A message on in is added to the group of its Correlation ID, and the count from done is set for the group with the same Correlation ID. A group is complete when its number of messages reaches its own count. It does not matter which comes first: the done message usually arrives before the loop body has finished, but it can also arrive after it. Both ports of Join Each must therefore get the Correlation ID of the same Each:
On the in port, set Correlation ID to the Correlation ID variable of the Each item. The variable is available on the last step of the loop body, because every message in the loop carries the variables of the item it started from.
On the count port, set Correlation ID to the Correlation ID variable of the Each done port.
Mapping the Correlation ID of an outer Each into the Join Each of an inner loop would merge the results of all the inner loops into one group that never reaches its count.
Demo flow
The demo flow loops over the list [1500, 100, 800] in parallel. The Slow step (a Code Block) takes as many milliseconds as the item says, and Join Each collects the results. The items finish, and arrive at Join Each, in the order 100, 800, 1500:
Create the flow with the POST /flows endpoint, or with the Appmixer CLI (appmixer flow create demo-each-join.json).
Last updated
Was this helpful?
