reactive.extended_task
reactive.extended_task(func=None)
Decorator to mark an async function as a slow computation. This will cause the function to be run in a background asyncio task, and the results will be available via the ExtendedTask object returned by the decorator.
Unlike normal async render functions, effects, and calcs, extended_task
async computations do not block Shiny reactive processing from proceeding. This means that they can be used to perform long-running tasks without freezing the session that owns them, nor other sessions.
However, this also means that they cannot access reactive sources. This is because processing of inputs and reactivity is not blocked, and so the reactive sources may change while the computation is running, which is almost never the desired behavior. If any reactive sources are needed by the computation, the decorated function must take them as parameters, and the resulting ExtendedTask
object must be invoked with the corresponding arguments.
Parameters
Returns
Type | Description |
---|---|
ExtendedTask[P , R ] | Callable[[Callable[P , Awaitable[R ]]], ExtendedTask[P , R ]] |
An ExtendedTask object that can be used to check the status of the computation and retrieve the result. |
Examples
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400
## file: app.py
import asyncio
from datetime import datetime
from shiny import App, reactive, render, ui
app_ui = ui.page_fixed(
ui.h5("Current time"),
ui.output_text("current_time"),
ui.p(
"Notice that the time above updates every second, even if you click the button below."
),
ui.layout_sidebar(
ui.sidebar(
ui.input_numeric("x", "x", 1),
ui.input_numeric("y", "y", 2),
ui.input_task_button("btn", "Compute, slowly"),
ui.input_action_button("btn_cancel", "Cancel"),
),
ui.h5("Sum of x and y"),
ui.output_text("show_result"),
),
)
def server(input, output, session):
@render.text
def current_time():
reactive.invalidate_later(1)
return datetime.now().strftime("%H:%M:%S")
@ui.bind_task_button(button_id="btn")
@reactive.extended_task
async def slow_compute(a: int, b: int) -> int:
await asyncio.sleep(3)
return a + b
@reactive.effect
@reactive.event(input.btn, ignore_none=False)
def handle_click():
slow_compute(input.x(), input.y())
@reactive.effect
@reactive.event(input.btn_cancel)
def handle_cancel():
slow_compute.cancel()
@render.text
def show_result():
return str(slow_compute.result())
app = App(app_ui, server)