Hi @spriteware
Good question! But it’s actually not about what it can do - what Dash polling / background callbacks can’t, more about being able to do the same without the need of background callback and and its dependencies eg. Redis & Celery. Lets consider the simple progress update example from the background callback docs:
event_callback implementation:
from dash import Dash, html, Input
from dash_event_callback import event_callback, stream_props
import time
app = Dash(__name__)
app.layout = html.Div(
[
html.Div(
[
html.P(id="paragraph_id", children=["Button not clicked"]),
html.Progress(id="progress_bar", value="0", hidden=True),
]
),
html.Button(id="button_id", children="Run Job!"),
html.Button(id="cancel_button_id", children="Cancel Running Job!", hidden=True, disabled=True),
]
)
@event_callback(
Input("button_id", "n_clicks"),
cancel=[(Input("cancel_button_id", "n_clicks"), 0)],
reset_props=[
("progress_bar", {"hidden": True}),
("cancel_button_id", {"hidden": True, "disabled": True}),
("button_id", {"disabled": False}),
]
)
def update_progress(n_clicks):
n = 5
yield stream_props([
("progress_bar", {"hidden": False}),
("cancel_button_id", {"hidden": False, "disabled": False}),
("button_id", {"disabled": True}),
])
time.sleep(1)
for i in range(1, n):
yield stream_props([
("progress_bar", {"value": str(i), "max": n}),
("paragraph_id", {"children": [f"Progress: {i * 20}%"]}),
])
time.sleep(1)
yield stream_props([
("paragraph_id", {"children": [f"Job Completed! Button clicked {n_clicks} times."]}),
("cancel_button_id", {"hidden": True, "disabled": True}),
("progress_bar", {"hidden": True, "value": "0"}),
("button_id", {"disabled": False}),
])
app.run(debug=True, port=11111)
In order to run this on a multi core setup and background callbacks, you would be required to run at least 3 containers - Dash, Redis & Celery. With event callbacks you just need Dash.
Another nice but subtle advantage is that you are less prone to bad internet connections. Every poll requires the complete http process, while the sse just keeps the connection open. So even if you throttle to 3G via the network tab, after the first byte got send, there is no delay between the updates.
That said. this actually only applies if your event_callback stays in the timeframe of a regular callback. If your callback has a longer exec time then 30 secs you would still be required to use background callbacks with Celery.