Blocking code while server call is running?

Hi,

Is it possible to block other client code from being executed, or component event handlers from being triggered, until the results of a server code call have been returned?

For example, I am using a server code call to get data from a google spreadsheet, which takes some time to load and parse into a dictionary. I do not want the user to be able to search that dictionary for values until all of the data has been retrieved and loaded into the dictionary, or else he may get the (false) alert that no data was found, when in fact it just has not been loaded yet.

Thanks

Couple of ideas :

  1. You could use an alert box with “dismissable” set to False and saying “Please wait …”.
  2. You could disable the form underneath for the duration of the call.

Both of those features are described in the documentation.

I liked this when I saw it:

Perhaps adding an animation while your searchable panel is loading/disabled as @david.wylie suggested as #2 would help the user know why they can’t enter any searches yet.

1 Like

Thank you, I guess my question is more about the event to use to determine whether it has finished or not.

I know for background tasks, we can do something like:

task = anvil.server.launch_background_task('mytask',responses)
if task.is_completed():
  print("finished")

but can we do the same with only normal server calls? Is there a way to do,

while(serverfunction is running)
  component.enabled = false

or something like this, without using timers?

You can do things some other way. Since server calls block the rest of the code, you can use it to your advantage

self.task_completed=False
anvil.server.call('something')
self.task_completed=True

Let me know if that is what you want

1 Like

I’m using Background task to retrieve big data sometimes. It can run even for long time.

So my first code add a small button component to app top nav bar that show progress as % of task completion and holds the main part of the code, including progress tracking of the task.

While it’s running you can work with different data and functions.

When it’s loaded it change color to green, show notification and text change to completed. You can push the button and the form will open with the prepared data.

For small data it’s easier just to disable buttons and show notification that will inform you when it is ready. So something similiar to solution proposed by divyeshlakhotia.
You can add to each function short check. If self.task_completed=False then show notification that data is being loaded and the user should wait.

By the way I use this loading class in my app that blocks any interaction on the rest of the app. I use it for lots of server calls in my app.

(You may want to change the colors based on your scheme)


from anvil import *
import anvil.js
from anvil.js.window import document

class Loading:
    def __init__(self,text=''):
        self.text=text
        
    def __enter__(self):
        html=f"""
<div class="bar-container">
    <div class="progress-bar">
        <div class="progress-bar-value"></div>
    </div>
    <center class='loading-prompt'>{self.text}</center>
</div>
<style>
.bar-container {{
  width: 100vw;
  height: 100vh;
  margin: 0px;
  position: fixed;
  top: 0px;
  
  z-index: 999999999;
  background-color: rgba(0,0,0,0.6);
}}

.progress-bar {{
  height: 6.5px;
  background: #002440;
  background-image: none;
  width: 100%;
  overflow: hidden;
}}

.progress-bar-value {{
  width: 100%;
  height: 100%;
  background-color: #15f4ee;
  animation: indeterminateAnimation 1.5s infinite linear;
  transform-origin: 0% 50%;
}}

.loading-prompt {{
  margin-top: 8px;
  padding: 10px 20px;
  background: #002440;
  position: relative;
  left: 50%; 
  color: white;
  transform: translateX(-50%);
  display:inline-block;
  font-size: 18px;
  border-radius: 10px;
}}

@keyframes indeterminateAnimation {{
  0% {{
    transform:  translateX(0) scaleX(0);
  }}
  40% {{
    transform:  translateX(0) scaleX(0.4);
  }}
  100% {{
    transform:  translateX(100%) scaleX(0.5);
  }}
}}
        """
        template=HtmlTemplate(html=html)
        self.loading_system=anvil.js.get_dom_node(template)
        document.body.appendChild(self.loading_system)
    
    def __exit__(self, *args, **kwargs):
        self.loading_system.remove()

And then I use it like this

with Loading('Please Wait'):
    anvil.server.call('something')
4 Likes

That’s a nice solution. Do you put the class into each Form you want to use it in, or could that be used in a Module as well?

I guess this was a simpler issue than I made it out to be. I didn’t realize the server calls were indeed blocking code (when I put an alert after the call I saw now that it doesn’t run until the server function completes), since components on the forms can still be interacted with, and other parts of the code can still run.

I use it in a Module since I want it to be accessible everywhere.

1 Like

Only the code that immediately follows the call, in the same function. Timers, and users’ mouse-clicks on buttons, links, etc., can still trigger other client-side functions, and they will run. Therefore, it is the responsibility of application code to disable those things before the server call, if that’s what it takes.

You can use the pattern that @divyeshlakhotia has shown.

1 Like

Trying to get the UI to do what you want sometimes is a never ending job. Tomorrow you may have a new UI element that doesn’t play nice with the blocking div, or, as @p.colbert said, there may be other reasons why an event is triggered, and you would need to keep using other safeguards.

In cases like this I like to:

  1. Explicitly and visibly disable select UI elements. This is more to give the info to the user that the app is ready or not to perform an action than to affect the app’s behavior.
  2. Use form/global status variables to decide whether to proceed with the execution. Something like if self.loading_data: return.

The first point makes the app’s behavior clear to the user, the second point makes it clear to the developer.

An invisible UI element that affects all, is, yeah, invisible to both user and developer.

2 Likes