Wait For Async Sub-Flows Node
Introduction
The Wait For Async Sub-Flows node (WAIT_FOR_ASYNC_SUB_FLOWS) blocks the parent flow at a join point after it has dispatched one or more async sub-flows (SubFlow nodes with async = true), until those sub-flows satisfy the configured release condition, and only then continues to the following nodes.
Its semantics are similar to Java's Thread.join() or CompletableFuture.allOf().join(): dispatch several async sub-flows in parallel, then wait at one point for them to finish (or reach a threshold) before continuing.
What problem it solves
By default, when a SubFlow node has async = true, the parent flow "returns immediately after dispatch" and keeps going without ever observing the sub-flows' success or failure. When the business needs to "process N sub-flows in parallel first, then wait for them to complete before aggregating/continuing", this node is used at the join point to wait.
Limitations
- In-memory flows only: this node can only be used in flows whose trigger has "Runs in Memory" enabled. A non-in-memory (persistent) flow containing this node will fail to save/publish with an error, and saving is blocked.
- Direct sub-flows only: the wait scope is the async sub-flows dispatched directly by the current flow instance (
parent_id = current flow instance). It does not recursively wait for grandchild sub-flows. If you need to wait for grandchildren, place another wait node inside the intermediate flow. - Timeout does not cancel sub-flows: a timeout only releases the parent flow; already-dispatched sub-flows keep running in the background.
Wait Scope
- The scope is delimited by flow instance: it waits for all
async = truedirect sub-flows dispatched "under the current flow instance, before this node". - Not divided by thread/iteration: if the async sub-flows were dispatched inside a
Repeat(loop) with parallel iterations, sub-flows from all iterations are included in the wait scope. - On entry the node scans all async sub-flows under the current instance (whether already finished or still running). "Fast sub-flows" that already completed before the parent reached this node are counted correctly and never dropped.
Completion Modes (completionMode)
The node offers four parallel completion strategies. The node's final status (success / failure) is uniformly determined by onConditionMet (when the condition is satisfied) and onConditionUnmet (when it is not), each with per-mode defaults.
| Completion mode | Wait / release condition | onConditionMet (default) | onConditionUnmet (default) |
|---|---|---|---|
| All completed (ALL_COMPLETED, default) | Wait for all sub-flows to reach a terminal state; only cares about completion, not success/failure | Node is always SUCCESS | — |
| All succeeded (ALL_SUCCESS) | Wait for all sub-flows to reach a terminal state; requires all to succeed | All succeeded → SUCCESS | Any failure → FAILURE |
| Failure threshold (FAILURE_THRESHOLD) | Early-release when failure count ≥ failureThreshold | Threshold reached → FAILURE | All done without reaching threshold → SUCCESS |
| Success threshold (SUCCESS_THRESHOLD) | Early-release when success count ≥ successThreshold | Threshold reached → SUCCESS | All done without reaching threshold → FAILURE |
Notes:
- Difference between "All completed" and "All succeeded": the former only cares whether sub-flows finished — the node succeeds regardless of outcome; the latter requires all to succeed — a single failure makes the node fail by default.
- "Early-release" in threshold modes means that once the threshold is met, the parent flow continues immediately, and the remaining running sub-flows are not cancelled — they keep running in the background.
- The table shows defaults. Both
onConditionMetandonConditionUnmetcan be manually changed to "success" or "failure" in the config panel to fit your business (for example, letting "All succeeded" mode release as success even when there is a failure).
Configuration
| Config | Description | Default |
|---|---|---|
| Completion mode (completionMode) | One of the four strategies above | All completed (ALL_COMPLETED) |
| Failure threshold (failureThreshold) | Required only for "Failure threshold" mode; must be ≥ 1 | None (validated at publish time) |
| Success threshold (successThreshold) | Required only for "Success threshold" mode; must be ≥ 1 | None (validated at publish time) |
| Node status when condition met (onConditionMet) | Success / failure | Per-mode default (see table above) |
| Node status when condition unmet (onConditionUnmet) | Success / failure | Per-mode default (see table above) |
| Status when nothing to wait for (notTriggered) | If there are no async sub-flows to wait for on entry, the node releases immediately with this status; set it to "failure" to make the flow error out when "there should have been sub-flows but there were none" | Success |
| Wait scope filter (waitForNodeIds) | Optional. Restricts waiting to sub-flows dispatched by specific SubFlow nodes. This is a scope filter, not a count contract — it does not mean "must have N". To express "must have N", use a threshold mode | Empty (wait for all async sub-flows) |
| Timeout (timeout) | Optional. Empty means wait forever | Empty |
| Timeout unit (timeUnit) | days / hours / minutes / seconds | seconds |
| Include sub-flow details (includeSubFlowDetails) | Whether the output includes each sub-flow's full output | false |
| Max tracked sub-flows (maxTrackedSubFlows) | Cap on the number of detail entries in the output, to avoid output bloat; only truncates the output details and does not affect the correctness of the wait decision | 500 |
Timeout
- After a timeout is configured, if the completion condition is still not met when the timeout fires, the node ends with a success / failure terminal status (via
onConditionUnmet, default failure), not a separate "timed out" status. You can branch to an error-handling node based on this. - A timeout does not cancel unfinished sub-flows; they keep running in the background.
- The output marks a timeout release with
timeout: trueandterminationReason: TIMED_OUT.
Output
After the node reaches its terminal state, the output (referenceable by subsequent nodes) includes:
| Field | Description |
|---|---|
nodeResult | Node terminal status: SUCCESS / FAILURE |
terminationReason | Reason code, e.g. ALL_COMPLETED / ALL_SUCCEEDED / SOME_FAILED / FAILURE_THRESHOLD_REACHED / SUCCESS_THRESHOLD_REACHED / TIMED_OUT / NO_ASYNC_SUB_FLOWS |
message | A localized one-line description of the result |
completionMode | The completion mode that triggered the release |
conditionMet | Whether the completion condition was met (true → onConditionMet, false → onConditionUnmet; timeout is false) |
total | Total number of sub-flows in the wait scope |
success / failure / pending | Counts of succeeded / failed / still-unfinished sub-flows |
timeout | Whether the node ended due to a timeout |
stillPending | On early-release or timeout, the list of sub-flow ids still unfinished |
subFlows | Summary of each sub-flow (sub-flow unique id, the node that dispatched it, node name, definition id, status; plus output when includeSubFlowDetails = true) |
warnings | Diagnostics (e.g. the node looks misplaced, details were truncated) |
Tip: the sub-flow identifier in
subFlowsisflowInstanceUniqueId(a UUID), consistent across in-memory and persistent modes.
In the flow instance view, this node only shows its output once it reaches the terminal state (DONE); nothing is shown while it is still waiting (IN_PROGRESS).
Typical Usage
- Dispatch several
async = truesub-flows in parallel using aRepeatloop or multipleSubFlownodes. - Place a Wait For Async Sub-Flows node after those dispatch nodes.
- Choose a completion mode:
- "Continue once all finish, regardless of outcome" → All completed.
- "Continue only if all succeed" → All succeeded.
- "Continue as soon as N succeed" → Success threshold + set the success threshold.
- "Abort early if too many fail" → Failure threshold + set the failure threshold.
- Optionally configure the timeout, the
onConditionMet/onConditionUnmetterminal statuses, whether to output sub-flow details, and so on.