Skip to main content

Repeating work

Twelve minutes. So far each flow has done one thing once. This one does the same thing to every item in a list — which is where automation starts paying for itself.

From here on the tutorials import a finished template and take it apart, rather than having you place fifteen nodes by hand. Reading a working flow and changing it teaches more per minute than transcribing a node list.

What you need

The flow you are building

The finished flow

Import it

  1. In the Flow Designer, open Start with an example on the home screen (or the Templates gallery).
  2. Find Copy Files and import it.
  3. Open the imported flow.

The loop

Three nodes make the loop, and the shape is worth learning because it recurs everywhere.

Label — "Loop Start" marks a point in the flow you can jump back to. On its own it does nothing.

ForEach — "For Each File" is the interesting one. It takes a list from msg and has two output ports:

PortFires whenWired to
1There is another item — it puts that item in msg and emits herethe work you want done per item
2The list is exhaustedwhatever comes after the loop

Its configuration reads:

PropertyValueMeaning
InputMessage → files_to_copythe list to walk
OutputMessage → current_filewhere to put the current item
IndexMessage → current_indexwhere to put the position, 0-based

GoTo — "Loop Back" jumps back to Loop Start after each item is handled.

So the cycle is: Label → ForEach → do the work → GoTo back to Label → ForEach … until ForEach runs out of items and emits on port 2 instead, which leads to Stop.

Why a GoTo rather than a loop body

Robomotion flows are graphs, not nested blocks — there is no "inside the loop" region on the canvas. ForEach remembers its position between visits, so jumping back to it with a GoTo is what advances the list. Once you have seen it once it reads naturally, and the same Label/GoTo pair is how Go to Label works generally.

The work inside the loop

Two nodes, and the first is doing something worth noticing:

var p = msg.current_file;
var lastSlash = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'));
msg.dest_path = msg.destination_folder + '/' + p.substring(lastSlash + 1);
return msg;

It takes the full source path of the current file, strips off everything before the last slash to get the bare filename, and joins that to the destination folder. It checks both / and \ so the same flow works on Linux and Windows.

Then Copy File (FileSystem → Copy) does the actual work, reading msg.current_file and msg.dest_path.

The detail that matters in production

Select Copy File and look at Continue On Error. It is switched on.

That means one unreadable file does not abort the run — the loop carries on with the next item. For a batch job that is almost always what you want: copying 99 of 100 files and reporting the one failure beats copying none.

The same option is on Create Dest Dir, so a destination folder that already exists is not treated as a failure.

This is the first appearance of a theme the next tutorial is entirely about: a flow that works is not the same as a flow that keeps working.

Run it

Run the flow. It asks for a destination folder, defaulting to a dest directory beside the fixtures. Accept it and the files are copied.

Then look at Branch On Cancel, a Function node with two outputs:

return (msg.destination_folder && msg.destination_folder.trim()) ? [msg, null] : [null, msg];

A Function node with multiple outputs returns an array, one slot per port. Put the message in a slot to emit there, null to stay silent. Here: a real folder goes out port 1 into the copying; an empty answer goes out port 2 straight to Stop.

So pressing Cancel ends the flow cleanly instead of copying files into nowhere. Handling the empty answer is the kind of thing that separates a flow that demos from a flow you can leave running.

Try changing it

  • Add a third entry to msg.files_to_copy in Seed Sources and re-run. The loop does not change — that is the point.
  • Add a Debug node between For Each File and Build Dest Path, then run. Watch current_file and current_index change on each pass. This is the clearest possible view of a loop executing.
  • Turn Continue On Error off on Copy File, point one entry at a file that does not exist, and re-run. Compare what happens.

Next

Handling failure → — what to do when a step fails outright, instead of just continuing past it.