How to cancel a running callback when an input changes?

I have a dashboard with a dcc.DatePickerRange whose default value is last month. So for example, on page load:
start_date = “2020-06-01”
end_date = “2020-06-30”

When start_date or end_date change, it triggers a query whose execution time is proportional to the number of days between start_date and end_date.

If a user is interested in, say, the June 2015 data, they will first update start_date to “2015-06-01”, and, by the time they change end_date to “2015-06-30”, the query for June 2015 to June 2020 will have been fired and they will have to wait for this lenghty query to finish. Only then, will the real June-2015-only query run and the figure be updated.

If I could cancel the running callback and start a new one when end_date is updated, that would solve my problem.

Is there any way to do that?

layout = html.Div(
    children=[dcc.DatePickerRange(id="date-range",), dcc.Graph(id="figure"),]
)


@app.callback(
    Output("figure", "figure"),
    [Input("date-range", "start_date"), Input("date-range", "end_date")],
)
def update_figure(start_date, end_date):
    data = run_lenghty_query(start_date, end_date)
    figure = make_figure_from_data(data)
    return figure

Bump, I have a similar question. I am using pyodbc and would like to cancel a running query if the callback changes.

As of today there is no way to cancel a running callback.

If you are going to run a lengthy callback I’d advise to have a Submit/Compute button that the user clicks when all the other parameters are set rather than triggering the callback every time one of the inputs changes.

Hello RenaudLN,

Thank you for your answer. I tried having a Submit/Compute button, but then there were no way of having the default date range and associated figure dispalyed on page load.

I tried using a hidden dropdown object as Input to fill the default start_date and end_date of a DatePickerRange object on page load.

Suppose that I have 3 callbacks to update 3 things when the page loads;

  • DateRangePicker.start_date
  • DateRangePicker.end_date
  • Button.n_clicks.

Additionnaly, Button.n_clicks is used as in Input for a callback that updates a Figure. This callback uses DatePickerRange.start_date / end_date as States to pass as parameters to generate the figure

Then, you could expect the figure to appear with the default date range on page load and the to be updated when the button is clicked. However, this doesn’t work because when the callback that clicks the button on page load is fired, the state of DatePicker.start_date / end_date hasn’t been set yet so their value is None.

Is there anyway to have the callbacks fired in a specific order (Date range first, button after) to avoid that?

ex. code:

layout = html.Div(
    children=[
        dcc.DatePickerRange(id="date-range",),
        html.Button("Submit", id="submit-button", n_clicks=0),
        dcc.Graph(id="figure"),
        dcc.Dropdown(
            id="hidden-dropdown", options=[], value="none", style=dict(display="none",),
        ),
    ],
)

# Page load callbacks

# On page load, set the default start date
@app.callback(
    Output("date-range", "start_date"), [Input("hidden-dropdown", "value")],
)
def _update_default_start_date(_):
    start_date, _ = get_current_month()
    return start_date


# On page load, set the default end date
@app.callback(
    Output("date-range", "end_date"), [Input("hidden-dropdown", "value")],
)
def _update_default_end_date(_):
    _, end_date = get_current_month()
    return end_date


# On page load, click the Submit button
@app.callback(
    Output("submit-button", "n_clicks"), [Input("hidden-dropdown", "value")],
)
def _click_button(_):
    return 1


# Click Submit
@app.callback(
    Output("figure", "figure"),
    [Input("submit-button", "value")],
    [State("date-rage", "start_date"), State("date-rage", "end_date")],
)
def _update_figure(_, start_date, end_date):
    # Problem : start_date and end_date are None when this is triggered
    # from the page load button click
    data = run_lenghty_query(start_date, end_date)
    figure = make_figure_from_data(data)
    return figure

@ajrheaume - I guess you could just use default values for the dates, i.e. something like,

import dash

def layout():
    return html.Div(
        children=[
            dcc.DatePickerRange(id="date-range", start_date=get_current_month(), end_date=get_current_month()),
            html.Button("Submit", id="submit-button", n_clicks=0),
            dcc.Graph(id="figure"),
            dcc.Dropdown(
                id="hidden-dropdown", options=[], value="none", style=dict(display="none", ),
            ),
        ],
    )

app = dash.Dash(prevent_initial_callbacks=True)
app.layout = layout  # assign the layout as a function to re-evaluate the dates on page load

@app.callback(Output("figure", "figure"),
              [Input("submit-button", "value")],
              [State("date-rage", "start_date"), State("date-rage", "end_date")],
              prevent_initial_call=False)
def _update_figure(_, start_date, end_date):
    data = run_lenghty_query(start_date, end_date)
    figure = make_figure_from_data(data)
    return figure

Any update on this?

Hi @mail8, if you are asking for cancelling running callbacks, you can use this:

1 Like