Loops & retry patterns
.loopTo(targetStageName) creates a back-edge to any earlier stage. The loop continues until a stage calls scope.$break(). This enables polling, pagination, retry, and iterative refinement patterns.
Basic loop
Section titled “Basic loop”import { flowChart, FlowChartExecutor } from 'footprintjs';
const chart = flowChart<{ counter: number; target: number }>('Init', async (scope) => { scope.counter = 0; scope.target = 5;}, 'init') .addFunction('Increment', async (scope) => { scope.counter += 1; if (scope.counter >= scope.target) scope.$break(); }, 'increment') .loopTo('Increment') .build();
const executor = new FlowChartExecutor(chart);executor.enableNarrative();await executor.run();// Narrative: "Looped back to Increment (pass 2)" ... "Looped back to Increment (pass 5)"Retry with backoff
Section titled “Retry with backoff”interface RetryState { attempt: number; maxAttempts: number; result?: string;}
const chart = flowChart<RetryState>('Init', async (scope) => { scope.attempt = 0; scope.maxAttempts = 3;}, 'init') .addFunction('CallAPI', async (scope) => { scope.attempt += 1; try { // Simulate flaky API if (Math.random() < 0.5) throw new Error('timeout'); scope.result = 'success'; scope.$break(); } catch { if (scope.attempt >= scope.maxAttempts) { scope.result = 'failed after retries'; scope.$break(); } // Wait before retry await new Promise((r) => setTimeout(r, scope.attempt * 100)); } }, 'call-api') .loopTo('CallAPI') .build();Pagination
Section titled “Pagination”interface PaginationState { page: number; allItems: string[]; hasMore: boolean;}
const chart = flowChart<PaginationState>('Init', async (scope) => { scope.page = 0; scope.allItems = []; scope.hasMore = true;}, 'init') .addFunction('FetchPage', async (scope) => { scope.page += 1; const items = await fetchPage(scope.page); // your API call scope.allItems = [...scope.allItems, ...items]; scope.hasMore = items.length > 0; if (!scope.hasMore) scope.$break(); }, 'fetch-page') .loopTo('FetchPage') .build();Narrative strategies for loops
Section titled “Narrative strategies for loops”When loops run many iterations, the narrative grows. Use loop-aware strategies to control output:
import { WindowedNarrativeFlowRecorder, AdaptiveNarrativeFlowRecorder, SilentNarrativeFlowRecorder,} from 'footprintjs';
// Keep first 3 + last 2 iterationsexecutor.attachFlowRecorder(new WindowedNarrativeFlowRecorder(3, 2));
// Full for first 3, then every 5thexecutor.attachFlowRecorder(new AdaptiveNarrativeFlowRecorder(3, 5));
// Summary only — "Looped 50 times"executor.attachFlowRecorder(new SilentNarrativeFlowRecorder());Try it live
Section titled “Try it live”- Loops —
loopTo()+$break() - Strategy Comparison — all 6 loop narrative strategies