What is the best practice for asynchronously loading component data?

I use a timer for non simultaneous background calls.

I add a timer to the form, set the interval to 0.1 and put the server call in the tick event with _s so it doesn’t show the spinner.

The timer allows to run code on the client in background, but it is going to be blocking. So I split a long call into many small ones.

Yeah, I was actually thinking about something like this. Would be nice if we could get an abstracted system for something like this with annotations (eg. being able to push events from the server’s background threads to the client)

If you feel it is something that many folks would find useful, you could make a feature request.

1 Like

I think being able to populate your UI asynchronously would be really, really nice, yes. Of course there are ways to optimize things as they are, but this would make everything a lot easier development wise.

I am a bit late to this thread. I think asynchronous calls would be amazing.
Maybe as an optional parameter to the anvil.server.call

I was also looking for ways to do this. Creating a background task as indicated, I feel at this stage is
the best approach.

Well, little late to the party… But I also needed to load component data in an aync manner.
In my case, I have a repeating panel with a complex layout, each one takes 2-5 seconds to populate including fetching image and scraping remote site data from an uplink server function.

Here is how it looks.

As you can see, populating a repeating panel with at least 10 items woud take 1 minute. which is unacceptable.

So, I hacked together the following approach.

  1. Added an extra column to my row => row['fetched'] = False
  1. Made the server call from a timer tick with 1/2 second interval
  1. On first tick event set the row['fetched'] = False and made the call
  1. On following tick events I fetch the row from app_tables.get(id='my_row_id')
  1. Check if my server_function changed row[‘fetched’] to True
  1. If if’s True I got the data, refresh data bindings and do whatever i need to do with newly fetched data.
  1. remove the timer from component
  1. If timer tick event reached a count of 100 and still not fetched my data, I remove the timer anyways to avoid forever loop.

Well, still no async right?
here is the key.

I run my server function in a thread

from threading import Thread

def long_test(row):
    for i in range(10):
        print(f"[{i}]ITERATION")
        row['name']=str(i)
        sleep(1)
    row['fetched'] = True


@anvil.server.callable()
def long_test_function(row):
    print(f"Thread Start========[{type(row)}]=========")
    Thread(target=long_test, args=(row,)).start()
    print("Thread end===========")

This worked for me!

Although I think there should be a more elegant way of acheiving this.

like,

anvil.server.call_async('my_server_func' , async_handler=self.handler_function)

handler_function checks periodically if async call returned or not... and acts accordingly!

Please share your thoughts and suggestions.

Don’t do that!
It is dangerous to create your own threads. If you start working with data tables or other services, you could have nasty side effects. Plus they could be killed at will without notifying you.

The correct way is to use background tasks.

The difference is that the server will take care of keeping everything safe when concurrent threads access the same services.

This sound almost like you are doing the row['fetched'] = False on the client. If this is not the case, then you are doing the right thing.

If you are really changing one column of one row from the client, you are:

  1. Doing something unsafe. Your form has access to that row → anyone can do anything on your row, and perhaps on your database. It’s always safer not to give write access to the client (I don’t even give read access)
  2. You are doing things the slow way. You are already calling the server to fetch something, the server should set that value during the same call. It would be much faster (I hope that’s what you are already doing)

See this post: MVC Architecture Demo - #13 by owen.campbell about a new feature in Anvil Labs (the experiments that might eventually make their way into Anvil Extras) allowing asynchronous server calls.

2 Likes

Just wanted to say - great looking site!

Is that all Anvil?

1 Like

Yes! :wink: with a little bit css for the responsive grid layout.

1 Like

That’s excellent news! :heart_eyes:

I made sure the write operations are thread safe. Other than that what are the dangers we talking about? And, In my case, I don’t care about the thread getting killed, All it’s doing is trying to get data in case certain columns in my row is missing.

row['fetched'] = False was indeed on the client code while i was testing, later i moved it to server.

  1. Without giving write access i lose the sweet flavor of anvil’s live proxy object.
  2. That server call takes about 5 seconds! so, doing this in a single call is actually slower in my particular case, that’s the reason i had to do all these!

I don’t know what are the dangers.

I had apps using threads before the background tasks existed, and they were crippled with bugs. I couldn’t print, accesses to the database were failing, they were crashing, they were failing because killed half way, etc.

The fact that you have been lucky so far using using threads doesn’t make it the right way. Background tasks have been designed to do that job, to be monitored, to report the status, to last longer than the caller’s request and than the 30 second timeout, and to play nice with all the rest of Anvil infrastructure.

Said that, there may be simple cases where threads don’t pose any risks and background tasks would introduce useless overhead.

It’s sweet and unsafe. It’s ok to use it when you play around, but you need to know that you are opening that sweet door for yourself and for the rest of the world. Whatever you can do from the form, any hacker can do it.

I have the feeling that the 5 seconds are the sum of the server call plus all the round trips triggered because you play with row objects on the client side. Obviously I don’t know if it’s 4+1 or 1+4.

I don’t know what you are doing with the row objects on the client side, but when someone talks on the same post about both using row objects on the client side and performance problems, 9 times out of 10 that’s the problem.

Summary: I saw a couple of flags to be risen, and I did rise them :slight_smile:

Ok, so since I read what @rsmahmud did yesterday I was thinking how I would do the same thing, using just:

  • components
  • timer components
  • background tasks

Today I made a short example of async like behavior that does what @rsmahmud wants, but uses background tasks instead:
async_with_timers

It can still be optimized further, especially if used with other techniques to chunk the server calls into blocks that only cover what the user might want to see on one page (like a lazy loading type behavior, etc.)

Anyway, here is a clone link, I made it as simplistic as possible to show off background tasks:
https://anvil.works/build#clone:OOPMWD7B4KU6QFH5=CLKCEKTKVAIOZA5YFPFWKBGL

2 Likes

Using one timer per component is a very nice approach.
The main form doesn’t even know about it!

One way to optimize it would be to have only one timer on the main form that makes one call that returns the result of all the background tasks that are ready at once. I assume a round trip is required every time a component calls task_value = self.task1.get_return_value(), so, instead of having 10 timers pinging the server at the same time, you only have one.

This solution would decrease the number of round trips, but would make the components more complex. The component should expose attributes like need_to_be_checked and set_result.


Why did you decide to remove it from the parent? I would have set the interval to zero.

#self.timer_check_btask.interval=0
self.timer_check_btask.remove_from_parent()

Docs for the non-blocking module of Anvil Labs are now at

https://anvil-labs.readthedocs.io/en/latest/guides/modules/non_blocking.html

And the demo mentioned earlier by @jshaffstall now includes error handling for those async server calls.

6 Likes

I tried both, I found it made no difference, it was the last thing I did after I posted the link. :man_shrugging:

I guess I was kind of just was wondering if it would also work? When I pushed it to like 100 elements it hangs while starting all of those background tasks.

Also, yes, my initial impulse was to create some kind of task stack to run through, with callbacks and raising events and all that, but then I decided to keep it as simple as I could to show off timers and background tasks if a new user wanted to clone it.

You could even create a background task to scroll through a list of tasks to monitor the other background tasks, using the task state of this ‘meta task’ to communicate the ID of each task and whether it was finished or not, using something like .task_state[task_id] :exploding_head:

1 Like

I guess that’s when it’s busy with round trips, each checking its own background task. With a stack you would have only one round trip per second. It would be slightly slower, but the app would not hang.

But for simple cases where you have the freedom to abuse the resources, the component with its own timer is the most elegant solution.

1 Like

Here is my use case,

each row contains details about a movie, columns are coming from 3 different websites, from an uplink server function scraping and parsing the sites with beautiful soup.

once the row has been populated with complete data set, no more server calls would be raised from it ever. component will call the server function only if it has incomplete data.

if one thread gets killed, no worries, next one will populate the data.

concurrent visitors might raise multiple calls for the same row, but my thread function takes care of that.

It’s just a weekend project and exploring anvil. So, I am not concerned about hackers.

1 Like

That’s nice!

But, for my case i don’t need to call the background task every time that component is on screen.

Only the first time ever it has been on any screen to populate it’s missing data.

I could have done it at the time of adding the row to database making sure it has complete data.

I thought why not populate it only on demand. Background tasks seemed too resource heavy for my use case. I can run a thousand threads but not nearly enough processes.