reactive.extended_task
=None) reactive.extended_task(func
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
: 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 reactive, render
from shiny.express import input, ui
ui.h5("Current time")
@render.text
def current_time():
reactive.invalidate_later(1)
return datetime.now().strftime("%H:%M:%S")
with ui.p():
"Notice that the time above updates every second, even if you click the button below."
@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
with ui.layout_sidebar():
with 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")
@reactive.effect
@reactive.event(input.btn, ignore_none=False)
def handle_click():
# slow_compute.cancel()
slow_compute(input.x(), input.y())
@reactive.effect
@reactive.event(input.btn_cancel)
def handle_cancel():
slow_compute.cancel()
ui.h5("Sum of x and y")
@render.text
def show_result():
return str(slow_compute.result())