Skip to main content

Background Tasks / Progress

Execute background functions while incrementing a progress bar.

import asyncio
import time
import concurrent.futures
from threading import Event
from h2o_wave import main, app, Q, ui


# This takes a lot of time (compute heavy).
def blocking_function(q: Q, loop: asyncio.AbstractEventLoop):
count = 0
total = 10
future = None
while count < total:
# Check if cancelled.
if q.client.event.is_set():
asyncio.ensure_future(show_cancel(q), loop=loop)
return
# This blocks the main thread and prevents any other execution.
# This would be the compute in the real world.
time.sleep(1)
count += 1
# If future is not done yet, skip the update to keep the correct order.
if not future or future.done():
# Assume you are able to emit some kind of progress.
future = asyncio.ensure_future(update_ui(q, count / total), loop=loop)


async def show_cancel(q: Q):
q.page['form'].progress.caption = 'Cancelled'
await q.page.save()


async def update_ui(q: Q, value: int):
q.page['form'].progress.value = value
await q.page.save()


@app('/demo')
async def serve(q: Q):
# Unimportant, draw initial UI.
if not q.client.initialized:
q.page['form'] = ui.form_card(box='1 1 3 2', items=[
ui.buttons([
ui.button(name='start_job', label='Start job'),
ui.button(name='cancel', label='Cancel')
]),
ui.progress(name='progress', label='Progress', value=0),
])
q.client.initialized = True

# Handle start job button click.
if q.args.start_job:
# Do not run like this - will block the whole thread - freeze the app.
# blocking_function(q, loop)

# Get the current event loop - will be used for
# running async functions within the blocking.
loop = asyncio.get_event_loop()
# Create an event to use for cancellation.
q.client.event = Event()
with concurrent.futures.ThreadPoolExecutor() as pool:
await q.exec(pool, blocking_function, q, loop)

if q.args.cancel:
q.client.event.set()

await q.page.save()

Tags:  background_tasksprogress