Docs/guide/control flow
3 min read

Control Flow

Guide to Budment's native graph control flow structures, including conditional branches, switch cases, fixed loops, and asynchronous polling.

Budment compiles control flow declarations into native graph nodes. Conditional paths and retries are evaluated directly inside the native Go runtime.

Control structures are natively executed by the Go FSM, allowing Budment to automatically track and aggregate execution paths. Every branch taken, match case hit, loop iteration, and poll attempt is inherently measured and pushed to the metrics sink without requiring custom metrics.counter() calls in your JavaScript hooks.

1. Branching: branch

The branch(condition, truePath, falsePath?) node splits execution based on the boolean result of a condition callback.

typescript
import { branch, http, get, log } from '@budment/sdk';

export default [
    http.get("https://api.example.com/user/profile")
        .after({ extract: { "role": "user_role" } }),

    branch(
        () => get("user_role") === "admin",
        // True Path (executed if condition evaluates to true)
        [
            http.get("https://api.example.com/admin/metrics"),
            log("Admin panel metrics retrieved.")
        ],
        // False Path (executed if condition evaluates to false)
        http.get("https://api.example.com/user/dashboard")
    )
];
  • Auto-Telemetry: Branch decisions are tracked atomically. The terminal summary outputs the exact hit distribution (e.g., branch_1: true 450 | false 550).

2. Multi-Way Matching: match

The match(condition, cases, defaultPath?) node acts as a declarative switch/case construct. It evaluates a condition returning a string or number and natively directs the worker to the matching pipeline.

typescript
import { match, http, get, log, sleep } from '@budment/sdk';

export default [
    http.get("https://api.example.com/orders/next")
        .after({ extract: { "status": "order_status" } }),

    match(
        () => get("order_status") || "unknown",
        {
            // Single node: Do not wrap in []
            "COMPLETED": log("Order completed. Skipping."),

            // Multi-nodes pipeline: Wrap in an array when chaining actions
            "PENDING": [
                http.post("https://api.example.com/orders/process"),
                sleep(0.5),
                http.get("https://api.example.com/orders/status")
            ],

            "CANCELLED": http.post("https://api.example.com/orders/archive")
        },
        // Fallback default path
        log("Unrecognized order status received.")
    )
];
  • Auto-Telemetry: Every matched case (e.g., "COMPLETED", "PENDING") is recorded by the engine's metrics aggregator automatically.

3. Iteration: loop

The loop(count, logicPath) node executes a sub-pipeline for a fixed number of iterations.

Inside the loop, the engine automatically injects a loop_index variable (a 0-indexed counter) into the worker's memory scope.

typescript
import { loop, http } from '@budment/sdk';

export default [
    loop(5, [
        http.post("https://api.example.com/messages/send")
            .before({
                body: {
                    message: "Ping",
                    index: "{{loop_index}}"
                }
            })
    ])
];

Scope Safety in Nested Loops: Budment's Go engine natively tracks memory scope. When entering a loop, it saves the previous state of loop_index (if any), and perfectly restores or deletes it after the loop completes, preventing variable collisions in nested iterations.

  • Auto-Telemetry: Total cycles generated by the loop node are collected instantly in the metrics sink.

4. Asynchronous Polling: poll

The poll(condition, logicPath, policy) node behaves as a native Do-While loop.

When the worker enters this node, it executes the action pipeline first, and only then evaluates the condition predicate. The cycle repeats until the condition returns true or the maxAttempts limit is reached.

typescript
import { poll, http, get } from '@budment/sdk';

export default [
    // 1. Trigger an asynchronous background report generation
    http.post("https://api.example.com/reports/generate")
        .after({ extract: { "job_id": "report_job_id" } }),

    // 2. Poll until the backend marks the job as READY
    poll(
        () => get("job_state") === "READY",
        [
            // This runs immediately on the first attempt, then checks the condition
            http.get("https://api.example.com/reports/status/{{report_job_id}}")
                .after({ extract: { "status": "job_state" } })
        ],
        {
            interval: "1000ms", // Pause timer between failed attempts
            maxAttempts: 10     // Bounded limit before exiting the poll
        }
    )
];
  • Auto-Telemetry: The engine captures the boolean result of the polling cycle (whether it succeeded or exhausted max attempts) and factors it into performance aggregates automatically.