plotly / dash-table

OBSOLETE: now part of https://github.com/plotly/dash
https://dash.plotly.com
MIT License
420 stars 74 forks source link

PreventUpdate will block the previous callback #925

Closed laoxu0212 closed 1 year ago

laoxu0212 commented 2 years ago

Here is an example: If you click A, it may take a long long time to process it(like 30s). Inside of this 30s, B is clicked and triggered the same callback, let's say after some conditional judgement, it decides not to do anything.

But it will eventually raise PreventUpdate, and stop the A in the process as well(so I won't see "A is clicked").

import dash
import dash_html_components as html
from dash_table import DataTable
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

import time

app = dash.Dash(__name__)

app.layout = html.Div([
    DataTable(
        id="table",
        columns=[{'name': 'A', 'id': 'A'}],
        data=[{'A': 'unclicked'}]
    ),
    html.Button(
        'A',
        id='A',
    ),
    html.Button(
        'B',
        id='B'
    )
])

@app.callback(
    Output(component_id='table', component_property='data'),
    [Input(component_id='A', component_property='n_clicks'),
     Input(component_id='B', component_property='n_clicks')],
)
def update_table(a_clicked, b_clicked):
    if dash.callback_context.triggered[-1]['prop_id'] == 'A.n_clicks':
        print('A is clicked')
        time.sleep(30)
        return [{'A': 'A-clicked'}]
    if dash.callback_context.triggered[-1]['prop_id'] == 'B.n_clicks':
        print('B is clicked')
        # After checking some data shortly less than 30s, decide not to change anything.
    raise PreventUpdate

app.run_server(debug=False)

This problem wasn't seen in 1.1.1, but recently I upgraded my dash and start seeing that.

Is there any mechanism to let me still see "A-clicked" even B is clicked during that 30s sleep?

canbooo commented 2 years ago

I am not sure if this is a bug or a feature. In any case, you should check out dash.no_update. Returning it for each output should avoid blocking the other callbacks.

laoxu0212 commented 2 years ago

I am not sure if this is a bug or a feature. In any case, you should check out dash.no_update. Returning it for each output should avoid blocking the other callbacks.

Thanks for you help, actually I have already tried it before, it has the same problem.

def update_table(a_clicked, b_clicked):
    if dash.callback_context.triggered[-1]['prop_id'] == 'A.n_clicks':
        print('A is clicked')
        time.sleep(30)
        return [{'A': 'A-clicked'}]
    if dash.callback_context.triggered[-1]['prop_id'] == 'B.n_clicks':
        print('B is clicked')
        # After checking some data shortly less than 30s, decide not to change anything.
    return dash.no_update

So I'm just trying to find a way to get it like 1.1.1

BenMoveo commented 1 year ago

Hi I'm facing this issue also as my callback gets triggered multiple times and as I allow the first one the following calls which raise PreventUpdate disable the one that I allowed. Is there any update on this issue?

T4rk1n commented 1 year ago

This repo is deprecated, dash-table has been incorporated into the main dash repo. For callbacks that takes longer than a couple seconds, we have background callbacks that offer better handling for this kind of situation.