Black Lives Matter. Please consider donating to Black Girls Code today.

How to deal with long, slow callbacks

I am wondering what to do to deal with a series of callbacks that tend to lock up my app.

I have a series of figures that are all tied to some selection of datasets. I plot all the figures with a plot button, as shown this post.

As I was developing this app, I noticed that there is no “debounce” method on the button clicks. So if my figures take a long time to load, and the user starts clicking on the plot button, then my app is hosed.

So, my first question is: Is there a clean way to “debounce” the buttons that are tied to figure callbacks?

To mitigate the multiple clicks effect, I have another callback to my plot button itself which sets the disabled property of the button. This is to prevent the user from repeatedly clicking on the plot button.

I have made a plugin (such and awesome feature of Dash!!) for a React component that serves as a data selector. It has an isDirty property that gets set with each data selection, and gets cleared on plotting. This does work, except for the fact that the disable_plot_button callback does not fire until after all of the figures have been updated. In other words, the user is still able to shoot himself in the foot while he is waiting for the plots to load.

So my second question is: How can we determine, and modify, the order in which the callbacks are executed? If I can’t have a debouncer on the button, then giving my disable_plot_button callback the highest priority would work.

1 Like

Here is an alternative view of the issue. I have, say, ten figures that need to be plotted, and a single plot button that causes all ten of them to be plotted. Regardless of the order in which the ten callbacks are triggered, it is always the case that the plot button should be disabled whenever a figure is updated. So it would make sense to have a any one of the figures target the plot button for callbacks. This is possible, since there can be many Inputs to a callback handler.

But now that the plot button has been disabled by the figure updates, I want another event to target the plot button in order to re-enable it. But I cannot do this, since this would require another callback handler with the plot button as its output.

As I understand it, there is design pattern to which Dash adheres and this requires us to only have a single callback to update a component. So, in under this pattern, how can I have figures disable the plot button and data selection enable it? Somebody, enlighten me on stateless design patterns for UI’s. :smiley:

What do you think of the way Shiny does this, where the plot is greyed out while being updated?
https://jjallaire.shinyapps.io/shiny-ggplot2-diamonds/
Doesn’t seem like it would exactly solve your problem, but as least lets the user know that the plot is in the process of being updated.

I believe that this needs to be solved (and perhaps configurable) by the dash front-end (dash-renderer). I don’t think that there is a clean solution for solving this in user-land.

This isn’t configurable and these UIs are difficult to create in user-land. I believe that the solution to these types of problems is to create an abstraction around loading and disabled states.

@jack Yes! I think this would be a good thing to have.

I have been looking for a “default” icon for a Plotly graph, but haven’t found anything. Does such a thing exist?

So, does this mean that you will fix it or will I have to fix it? :smiley:

I am interested in learning more about this, but I don’t quite follow you. Are you suggesting a React/Javascript abstraction layer that wraps the Dash components? I feel like this would defeat the purpose of using a framework like this. But I am all ears.

Maybe there is some other pattern that I simply haven’t thought of yet… I am still a beginner at this. :joy:

I do still have the question about callback order. In an ideal case, where all the callbacks return within 10’s of milliseconds, it is hard to notice that there is an order in which callbacks are executed. Everything seems to be instantaneous and asynchornous.

For my plot button, the callback should be very short, since it only touches the disabled property. In principle, this should be handled first. The figure callbacks, on the other hand, are very slow in comparison (although I can still do some refactoring to make them a bit shorter, and also use caching to make things feel faster).

In the microcontroller world, there is a concept of interrupt priority. In Dash, would it be possible to get priorities like Handle_this_callback_soon_plz and OMGOMGOMG_DO_THIS_THING_NOW!!!?

These two levels of callback priority could easily handle most usage cases for callbacks where you are either

  1. updating a UI element (fast) or
  2. Retrieving data and updating a figure (slow)

Yup! This will be done in a few stages. I added preliminary support for app-wide loading screens today, see 📣 Dash Loading States for more details. Next up will be a better abstraction around loading states on a per-component basis (i.e. adding a loading screen or spinner only on the components that are updating). The per-component abstraction will take a little bit longer to figure out.

2 Likes

Its a little verbose to setup, but I think its possible like this:

You can have a single callback and have it depend on all the Inputs can either enable or disable it.
Now the problem is you don’t know which is was fired more recently so have hidden

fields that store timestamps. Define separate callbacks that update each of the timestamps per each Input ie each of the graphs finishing. (this could be 10 separate timestamps or maybe just one enable timestamp for all 10 graphs at once) And then a separate timestamp for your enable event.
Then also list timestamps as State inputs to the single callback. In the original callback compare timestamps and act on whichever one is most recent to enable or disable… etc.

So basically not think of stateless, but think timestamps in hidden field as state.

For future reference, here is an example app shared by a community member for dealing with long, slow callbacks: Boilerplate Heroku Dash App for Long Processes (show and tell)

1 Like