Skip to main content

Streaming Progress Bar

A page that runs a long task on the backend and streams progress updates to a visual progress bar in real time using a generator function.

import time
from abstra.pages import register_function

@register_function
def run_pipeline():
steps = [
"Connecting to database...",
"Fetching records...",
"Processing batch 1/3...",
"Processing batch 2/3...",
"Processing batch 3/3...",
"Generating report...",
"Done!",
]
for i, step in enumerate(steps):
time.sleep(1) # simulate work
yield {"step": step, "progress": round((i + 1) / len(steps) * 100)}

@register_function
def __render__():
return """
<script src="https://cdn.tailwindcss.com"></script>
<style>body { margin:0; font-family:'Inter',system-ui,sans-serif; background:#f8fafc; }</style>
<div class="min-h-screen flex items-center justify-center">
<div class="w-full max-w-md bg-white rounded-2xl p-8 shadow-sm border border-slate-100">
<h1 class="text-xl font-bold text-slate-800 mb-6">Data Pipeline</h1>
<div class="mb-4">
<div class="w-full bg-slate-100 rounded-full h-3 overflow-hidden">
<div id="bar" class="h-full bg-indigo-600 rounded-full transition-all duration-300" style="width:0%"></div>
</div>
</div>
<p id="step" class="text-sm text-slate-500 mb-6 h-5"></p>
<button onclick="start()" id="btn"
class="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-2.5 rounded-xl font-medium text-sm transition-all">
Start Pipeline
</button>
</div>
</div>
<script>
async function start() {
const btn = document.getElementById('btn');
btn.disabled = true; btn.textContent = 'Running...'; btn.classList.add('opacity-75');
for await (const update of run_pipeline()) {
document.getElementById('bar').style.width = update.progress + '%';
document.getElementById('step').textContent = update.step;
}
btn.disabled = false; btn.textContent = 'Start Pipeline'; btn.classList.remove('opacity-75');
}
</script>
"""

Key patterns: Generator function with yield, for await...of inside async function (works in normal <script>), real-time progress bar, structured progress objects.

Consuming generators in JavaScript

Generator functions return a stream object that supports three patterns:

// 1. .forEach(callback) — works in any <script> tag
run_pipeline().forEach(update => {
console.log(update.step);
});

// 2. for await...of — needs async context (async function or <script type="module">)
for await (const update of run_pipeline()) {
console.log(update.step);
}

// 3. await — collects all chunks into an array
const updates = await run_pipeline();