The execution order, priority of reactive expressions

2,046 views
Skip to first unread message

xhd...@umd.edu

unread,
Apr 18, 2017, 10:06:50 AM4/18/17
to Shiny - Web Framework for R
I searched and found this question before. From the answers I assume there is no direct way to specify the execution order of some related reactive expressions. There is outputOptions, but it didn't give me the desired result.

Because the problem involves quite some moving parts it's difficult to comprise a minimal working example within a short time. For now I'm showing a screen recording of app, debug information, and the app source code. If this is still too complex to see the problem, I can try to see if I can get a example easily.

The screen recording is here. Please turn on the subtitle/CC, I added the notes below in the video.


Basically after I clicked a button, the data set is updated, and several plots/table need to be updated. The scatterplot have an optional layer depend on the row selection of a DT table. Ideally after the DT table update the row selection will be cleared, then that layer should not be drawn.

From the debugging process, you can see when that option layer was updated, the row selection value is still there, which caused points with NA values. This doesn't cause real problem in the screen recording because the row number 2 didn't match a row in the table, so NA points were ignored by ggplot. However when I selected the first point it will match wrong data and draw it in the plot.

I have tried different methods to fix this:

1. outputOption to set priority of plots. This didn't fix the problem.
2. In the code of action "remove selected", I tried to clear the row selection in the DT table, also the brush value in the histogram. However you can see from the screen recoding that it didn't work. Supposedly they have been executed after I clicked the button, and I also tried to execute them manually in the debug session, however the row selection, brush value are still there.

Note I have used "deferUntilFlush = FALSE" in the DT proxy.

The github repo have all the source code, you should be able to repeat what I do in screen recoding by setting a breakpoint or "browser()" at line 719 of server.R.

Is there any other method to make sure some part get updated first before other parts?



xhd...@umd.edu

unread,
Apr 18, 2017, 10:15:12 AM4/18/17
to Shiny - Web Framework for R
I observed my app closely again. It seemed my code of resetting row selection and brush does work, only a little bit later. So the app will draw the option layer with wrong data, then update the plot again to remove it.

Adding something like "invalidLater" may alleviate the problem, but that is obviously not optimal because we don't know how long is needed.

xhd...@umd.edu

unread,
Apr 18, 2017, 10:18:09 AM4/18/17
to Shiny - Web Framework for R
OK, the update is not caused by the resetting. There is a "distance" tab in same page, which have similar logic. I didn't add the resetting row selection and brush in that tab, but it have same behavior:
- select points, remove points
- some points still get highlighted (blue rectangle drawn around points), then removed in next update

Joe Cheng

unread,
Apr 18, 2017, 12:03:04 PM4/18/17
to xhd...@umd.edu, Shiny - Web Framework for R
This is a great question, thanks for providing the screencast which I'm sure took some effort to put together.

If I understand the problem correctly (and I'm only 60% sure I do--I haven't actually run your example yet), there are two parts to the solution. TL;DR: observer priorities, and freezeReactiveValue.

First, the observe(), observeEvent(), and outputs (via outputOptions()) can all take a priority field. If two observers/outputs have different priorities and both are invalidated, it's guaranteed that the one with the greater value for priority will be executed first (so priority=20 will happen before priority=10). The default priority is 0. So you should set the observers that reset brush, etc. to >0.

That won't solve the problem though; as you note, resetting row selection and brush only works "a little bit later". What is actually happening is that Shiny tries to stick to a "single source of truth", meaning, the true value of e.g. a brush exists only on the client; the value you read on the server, via input$brush, is just a reflection of what's on the client. So session$resetBrush doesn't directly change input$brush, but rather, sends a message to the client requesting that the brush is reset; and after the client has processed that message and actually reset the brush, it sends a message back to the server indicating the new value of the brush. This, as you note, can take an indeterminate amount of time.

Most of the time this approach works well enough, but in cases like yours this lag means there is a brief period (i.e. the rest of this reactive turn of the crank) where you have incorrect values in the `input` object. This usually manifests itself as a flicker (where the app briefly shows a nonsensical or erroneous set of outputs, then within a split second recalculating and giving the desired outputs), but in your case perhaps because data is actually being modified and saved during that period, your copy of the data frame actually gets corrupted (in the sense that the wrong rows have permanently be cleared or whatever).

You can fix this problem using the freezeReactiveValue function. At the time you call session$resetBrush (literally on the line above or the line below that call) you should call freezeReactiveValue(input, "brushId"). And the same for when you request a change to the row selection. What this basically does is put that particular input value in a frozen state, from that instant until the reactive graph reaches a temporary equilibrium (i.e. the rest of this turn of the crank, i.e. all outputs and observers have had their chance to execute). "Frozen state" means that any attempt to read the reactive value will fail in the same way that req(FALSE) would cause it to fail--silently.

So I'd try those two things--if they don't help, let us know.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/6a8632d6-6cca-4195-ae45-73d62f2a2f94%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

xhd...@umd.edu

unread,
Apr 18, 2017, 3:09:48 PM4/18/17
to Shiny - Web Framework for R, xhd...@umd.edu
Joe, thanks a lot for the detailed explanation!

I used "freezeReactiveValue" and it solved the problem. Basically it put the plot on hold (the plot become blank) until most of the reactive expression updated, then the plot updated correctly. I tried the priority before with the outputOptions, which didn't work, just like you said. Now the "freezeReactiveValue" itself can ensure the order of execution, which is just like a "req" in proper time.

Joe Cheng

unread,
Apr 18, 2017, 8:47:50 PM4/18/17
to xhd...@umd.edu, Shiny - Web Framework for R
Great, glad to hear it!

Erick Facure Giaretta

unread,
Mar 23, 2021, 3:59:43 PM3/23/21
to Shiny - Web Framework for R
Joe u solved my problem too, thank you!
Reply all
Reply to author
Forward
0 new messages