Should I use an Interval for a one-off delay?

I have an app where I’d like a delay in between callbacks. That is, I’d like to respond to a user event by updating a part of the display, waiting 2 seconds, then updating another part of the display.

The best solution I’ve found for this so far is to use a dcc.Interval(): at the time of the first callback I instantiate the Interval, and then when it fires 2 seconds later, I perform the second update. However, only want to make this second update the first time the interval fires, not every 2 seconds, so I’ve got code in my callback along the lines of

if n_intervals==1:
    <do stuff and return some updated UI element>
else:
    raise Exception()  # causes Dash to ignore this callback

The Exception prevents my callback from returning None (or something inappropriate) every 2 seconds after the initial interval has passed.

This means my app, from then on, is raising exceptions every 2 seconds so long as the user doesn’t interact with it. This seems a bit silly. I could set something to destroy the Interval() object after, say, 4 seconds, but I wonder if I’m just taking the wrong approach altogether. Is an Interval() the right way to approach my use-case where for any given user interaction I only want to generate a one-off delay?

Thanks for any advice!

Maybe your second part of the display could take as an Input whatever changed in the first part of the display? To wait 2 seconds, maybe you could put a time.sleep in that second callback?

layout = html.Div([
    html.Button(id='button'),
    html.Div(id='first-part'),
    html.Div(id='second-part'),
])

@app.callback(Output('first-part', 'children'), [Input('button', 'n_clicks')])
def update_first_part(...):
    ...

@app.callback(Output('second-part', 'children'), [Input('first-part', 'children')])
def update_second_part(...):
    time.sleep(2)
    ...

Thanks @chriddyp! This is a much simpler solution. Presumably update_second_part() could alternatively take Input('button','n_clicks') as input.

I think I went for Interval() following some vague idea that it would work better with multiple users/sessions, but I can’t see any reason why sleep() wouldn’t work exactly as well. The delay is server-side now but it’s still per-session, there is presumably no problem? No idea why I avoided it!

I have a complication: unfortunately, I actually need to update the display, wait 2 seconds, then update the same part of the display.

This means I’d end up with code like

@app.callback(Output('first-part', 'children'), [Input('first-part', 'children')])
def update_first_part_again(...):
    time.sleep(2)
    ...

which, understandably, is not allowed, and in any case would lead to as much of an infinite polling loop as the Interval() solution did (although minus the exceptions).

Any workaround I can think of for this still involves a component dependency loop, i.e. something where the callbacks trigger A -> B -> A or, in the simplest case above, just A -> A. I think such loops are not allowed in Dash at all, regardless of length?

The Interval() solution is working. FWIW it means I have code roughly like

@app.callback(Output('first-part', 'children'), [Input('timer', 'n_intervals')])
def update_first_and_only_part(n_intervals):
    if n_intervals == FIRST_ACTION_TIME:
        return firstUpdateResult()
    elif n_intervals == SECOND_ACTION_TIME:
        return secondUpdateResult()
    else:
        raise Exception()

where the timer is reinstantiated, and the count restarted, by the user interaction. This avoids having the component update itself, or any kind of loop.

I was thinking I could get a handful of exceptions instead of an infinite set by destroying the timer after a while. There’s no direct loop involved since a callback can take the timer as input and the Div holding the timer as output, but I think this will break things as it is effectively still a callback loop.

I just feel there must be a better approach that I’m missing! I suspect something really obvious. If not I can go with this. Thanks again for the help.

Hm, yeah this is a tough one. You’re correct in thinking that looped dependencies aren’t supported right now (but i think there is a case for them to be supported in the future).

To “destroy” the timer you can just set a callback to update the ‘interval’ property to a really large number.

To “destroy” the timer you can just set a callback to update the ‘interval’ property to a really large number.

This is a useful idea. My dilemma is what should trigger said callback. At the moment, in my code, it naturally would be triggered by the timer! I.e. I want the timer to count a certain number of intervals and then stop itself. Which creates a loop (either of the not-allowed kind or of the sneakily-allowed-but-self-triggering kind). If I trigger the timer turnoff from the UI component that got updated by the timer, I again have a loop, just a slightly longer one.

Edit: I’ve just understood that this probably isn’t a loop - I can trigger on n_intervals and update interval, so long as changing interval doesn’t cause n_intervals to automatically reset or anything. This is probably a good solution! But I’ll leave the rest of my post below just in case it’s of interest.


When I put it this way, the cleanest solution to my problem would be if dcc.Interval() supported the idea of counting a certain number of ticks and then stopping by itself, i.e. had something like a max_intervals parameter. I’m not sure if you have any other use-cases for that but if so, maybe it’s a feature suggestion?

The really clean solution my problem would be if a callback could say “do A, wait 2 seconds, then do B”, but that is totally contrary to structure of the framework I think.

1 Like

Yeah, that’s a good idea! I just made an issue in the github repo about it here: Ability to set a maximum number of intervals? · Issue #222 · plotly/dash-core-components · GitHub

1 Like