Asynchronous Logic Progress Bar Update

Hello everyone! I was trying to implement a dash bootstrap component progress bar on my dash app that would show the progress of Python function that take a long time to occur. As it stands I have a single button that is used to trigger a callback which invokes the function in question. The issue I have lies with dash updating a progress bar (Progress - dbc docs) from the middle of an ongoing callback. I tried finding existing solutions to my problem and the only one I found relied on the removed EVENT dash.dependencies state. I was hoping someone could point me in the right direction on how I could go about solving this issue. If it helps, here is the existing but outdated solution I found: Asynchronous logic · Issue #57 · plotly/dash · GitHub

Edit: I saw the comment in the initial thread referencing Redis and celery but was wondering if there was another solution available?

Please let me know if I can do anything to help make my question clearer!

Here’s a self-contained example using Redis and RQ (which is maybe a little simpler than Celery?).

In general, if you have a long running task it’s best to run it in the background and have your front end periodically check for completion. I’m not aware of a way to send updates to e.g. a progress bar from inside a callback.

1 Like

If you have a loop inside your callback, you could write state to a server side resource and pull that state periodically from the client.

Is the callback not blocking in this case or is there a way around that?

The callback will be blocked if you are only running a single thread. In that case, you would need to run the background process async to avoid the block. Here is a small example using the (default) threaded mode of Dash,

import time
import dash_html_components as html
import dash_core_components as dcc
from dash.exceptions import PreventUpdate
from dash_extensions.enrich import Output, Dash, Trigger, FileSystemCache

steps, sleep_time = 100, 0.1
# Create example app.
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    html.Button("Click me", id="btn"), html.Div(id="progress"), html.Div(id="result"),
    dcc.Interval(id="interval", interval=500)
])
# Create a server side resource.
fsc = FileSystemCache("cache_dir")
fsc.set("progress", None)


@app.callback(Output("result", "children"), Trigger("btn", "n_clicks"))
def run_calculation():
    for i in range(steps):
        fsc.set("progress", str((i + 1) / steps))  # update progress
        time.sleep(sleep_time)  # do actual calculation (emulated by sleep operation)
    return "done"


@app.callback(Output("progress", "children"), Trigger("interval", "n_intervals"))
def update_progress():
    value = fsc.get("progress")  # get progress
    if value is None:
        raise PreventUpdate
    return "Progress is {:.0f}%".format(float(fsc.get("progress")) * 100)


if __name__ == '__main__':
    app.run_server()
3 Likes

Ah makes sense, that’s neat!

1 Like

Hi @Emil,

Thanks for your information, It can let me update progress value periodically without a callback.

One more problem:
When I used FileSystemCache lib, and save “cache_dir” on my local side, I met this warning message=>

delete key ‘progress’ → [WinError 32] The process cannot access the file because it is being used by another process: 'cache_dir\3c709b10a5d47ba33d85337dd9110917’

Do you have any solutions that can remove this warning message? thanks.

Hello, I getting ImportError: cannot import name ‘Input’ from ‘dash’
is this an old dash package? ‘dash_extensions.enrich’

Have you installer Dash 2.x.x?