Speeding up Client Code to Avoid Timeouts and "Server code took too long"?

What I’m trying to do:
Hey all- I’m in desperate need of help. I’ve spent the past 6 months building a robust AI app and am on the ‘Professional Plan’. I deployed it 4 weeks ago and it was working, but now I’m getting hit with constant "anvil.server.TimeoutError: Server code took too long" pop up errors. I’m a mediocre programmer, and this app has grown significantly, but this still doesn’t make sense. I’ve speed up the code as fast as I can, I utilize background tasks, launchers, timers (to check for cell contents to tell me a background task is complete).

This app takes the contents of text boxes (like company name and url, ranging from 4 to 15 text boxes), and creates variables, saves the data in the table, then executes server function ‘launchers’, then executes background tasks. There’s a lot of calculations- but I still don’t know where the bottleneck is. It does calcs, executes about 3 out of 5 functions, then times out when it does that loop checking if product boxes are empty and launching that server/background task: deepdive_product_{i} etc.

What I’ve tried and what’s not working:
I’ve read the forums (believe me!). I’ve spliced up tasks, I’ve shortened code, I’ve even sent emails to sales and support asking if this is a sudden issue with their server, or if upgrading will help. I haven’t really gotten an answer.

Code Sample:

CLIENT SIDE CODE

def go_get_all_assets_click(self, **event_args):
        
        # Stop the function if any of the fields are empty
        if not self.company_name_input.text or not self.company_url_input.text 
            anvil.js.window.alert("Please fill in all the required fields before generating the full description.")
            return
        else:
          # Check if the URL starts with "http" (case-insensitive)
          company_url = self.company_url_input.text.strip()
          product_url = self.product_1_url_input.text.strip() 
          if not company_url.lower().startswith("http") or not product_url.lower().startswith("http"):
              anvil.js.window.alert("Company and Product URLs must include 'http'. For example, use 'https://www.apple.com/', not 'www.apple.com' ")
          else:
           
              first_run_complete_row = self.user_table.get(variable='first_run_complete')
              first_run_complete_row['variable_value'] = 'yes'
              first_run_complete_row.update()
  
              # COMPANY NAME
              company_name = self.company_name_input.text
              self.company_name = company_name
              # Save company name
              company_name_row = self.user_table.get(variable='company_name')
              company_name_row['variable_value'] = company_name
              company_name_row.update()
  
              # COMPANY URL
              company_url = self.company_url_input.text
              # Save company url
              company_url_row = self.user_table.get(variable='company_url')
              company_url_row['variable_value'] = company_url
              company_url_row.update()

              # Brand Tone
              brand_tone_title = self.company_name_input.text
              # Save Brand Tone 
              brand_tone_title_row = self.user_table.get(variable='brand_tone')
              brand_tone_title_row['variable_title'] = brand_tone_title
              brand_tone_title_row.update()
            
              # LAUNCH THE TASKS
              self.undertaken_tasks = []  # List to store all task IDs
            
              self.launch_draft_company_summary_scraper(company_name, company_url, self.user_table)
              self.launch_brand_tone_research(self.user_table, company_url)
              self.draft_deepdive_product_generators(self.user_table, company_name)


 ### THIS IS THE LOOP THATS CAUSING THE ERROR
def launch_draft_deepdive_product_generators(self,user_table, company_name):
    # Launch the background tasks concurrently
    tasks_product_research = []
    tasks_avatar = []
              
    for i in range(1, 6):
        # Get the product name and url from the textboxes
        product_name_input = getattr(self, f"product_{i}_name_input").text
        product_url_input = getattr(self, f"product_{i}_url_input").text

        # Check if the product name is not empty and save it to the user table
        if product_name_input:
            product_name_row = self.user_table.get(variable=f"product_{i}_latest")
            product_name_row['variable_title'] = product_name_input
            product_name_row.update()

            product_url_row = self.user_table.get(variable=f"product_{i}_url")
            product_url_row['variable_value'] = product_url_input
            product_url_row.update()

            self.undertaken_tasks.append(f"product_{i}_latest")
            print(f"Added to undertaken_tasks: product_{i}_latest")
            self.check_all_tasks_timer.enabled = True
            self.check_all_tasks_timer.interval = 3

            anvil.server.call(f"launch_draft_deepdive_product_{i}_generator", self.user_table, company_name, product_name_input, product_url_input)
            #task_product_research = anvil.server.call(f"launch_draft_deepdive_product_{i}_generator", self.user_table, company_name, product_name_input, product_url_input)
            # print(f"product_{i} analysis initiated")

            getattr(self, f"task_check_timer_product_{i}").enabled = True

          # Loop through avatars 1 to 3 for each product
            for j in range(1, 4):
                # Get the avatar description from the textbox
                avatar_input = getattr(self, f"avatar_{j}_product_{i}_input").text

                # Check if the avatar description is not empty and save it to the user table
              
                if avatar_input.strip():
                    print(f"Avatar {j} for product {i} input: '{avatar_input}'")
                    getattr(self, f"task_check_timer_product_{i}_avatar_{j}").enabled = True

                  # Launch the background task for Avatar
                    #anvil.server.call(f"launch_draft_deepdive_avatar_{j}_product_{i}_generator", j, i,self.user_table, company_name, getattr(self, f"product_{i}_name_input").text, avatar_input)
                    # Launch the background task for Avatar
                    anvil.server.call('launch_draft_deepdive_avatar_generator', j, i, self.user_table, company_name, getattr(self, f"product_{i}_name_input").text, avatar_input)

                    #task_id_avatar = anvil.server.call(f"launch_draft_deepdive_avatar_{j}_product_{i}_generator", self.user_table, company_name, getattr(self, f"product_{i}_name_input").text, avatar_input)
                    # print("Deep Dive Draft Avatar Research Started")

                    # Save it as the preview
                    variable_name = f"avatar_{j}_product_{i}_preview"
                    print(f"Searching for: {variable_name}")
                    avatar_preview_row = self.user_table.get(variable=variable_name)
            
                    if avatar_preview_row is None:
                        print(f"No data found for: {variable_name}")
                        continue
                    else:
                      avatar_preview_row['variable_value'] = avatar_input
                      avatar_preview_row.update()

                    getattr(self, f"task_check_timer_product_{i}_avatar_{j}").enabled = True
                    getattr(self, f"task_check_timer_product_{i}_avatar_{j}").interval = 3
                  
                    # # Step 2: Append the identifier to the list
                    self.undertaken_tasks.append(f"avatar_{j}_product_{i}_latest")
                    print(f"Added to undertaken_tasks: avatar_{j}_product_{i}_latest")

SERVER SIDE CODE

# HERE'S THE FUNCTION CAUSING ISSUES...
@anvil.server.callable
def launch_draft_deepdive_product_1_generator(user_table, company_name, product_name, product_url):
    anvil.server.launch_background_task('deepdive_draft_product_1_generator', user_table, company_name, product_name, product_url)

@anvil.server.background_task
def deepdive_draft_product_1_generator(user_table,company_name,product_name,product_url):
#### A BUNCH OF WORK, then when it's done...
# Save it in the table:
    product_1_latest_row = user_table.search(variable='product_1_latest')[0]
    product_1_latest_row['variable_value'] = product_summary
    product_1_latest_row.update()
    print("Product Research Complete")

Clone link:
It’s too robust to share, so I was hoping someone could share their theories?

Humans are notoriously bad at accurately guessing the location of bottlenecks. We often find that the problem is not where we expected. It’s hard for anyone – including you! – to theorize without data.

So, I suggest that it’s time to make some data (time measurements)! A few other suggestions:

  1. Sprinkle the ill-behaved function(s) (whichever one(s) that’s timing out) with “print timestamp” statements, to see for yourself where the execution time is going. (You’ll find the results in your corresponding Anvil logs.)
  2. Give each “print” its own unique output tag, so that, when reading the timestamps, you know what point in your code each timestamp applies to.
  3. Search this forum for other performance-measurement tips and tricks you can use.

Once you see where the time is going, then you can step back and look for strategies and tactics to combat the problem. And folks here on the forum will have some actual data they can look at, to make informed suggestions.

3 Likes

Are you passing an entire table to your server function?

That is, does your client code have a line like this?

self.user_table = app_tables.user_table

I doubt it’s causing the timeout, but that’s what caught my eye. You could instead just pass the name of a table.

It’s impossible to figure out what’s wrong without testing it, but I do see a few things that can be optimized.

I don’t know what self.user_table is, but, judging by the comments and by what you do with it, I’m going to assume that it is a table.

Your functions are full of get a row, change a field on it, update it.

You call row.update() many times. Every time you call this function on the client there is (maybe) a time expensive round trip. The update function is used when you want to change multiple columns at once. Used without parameters, either does nothing, or it wastes a round trip for no reason. You can remove all the row.update() lines and everything will work exactly the same way.

Every time you run these three lines, you execute three time expensive round trips. A better way to do that would be to create a server function that takes in input all the texts you are processing, run all that logic on the server side, and return something that lets the client know how it went. Something like this:

# on the client
    result = anvil.server.call('get_all_assets', 
        self.company_url_input.text, 
        self.company_name_input.text,
        [...])
    if result != 'OK':
        alert(result)
        return
    # everything is ok, do what you need to do

# on the server
@anvil.server.callable
def get_all_assets(company_url, company_name, [...]):
    if not company_name or not company_url:
        return 'Please fill in all the required fields before generating the full description.'

    if not company_url.startswith('http'):
        return 'Must include http'
    
    row = app_tables.user_table.get(variable='first_run_complete')
    row['variable_value'] = 'yes'
    
    row = app_tables.user_table.get(variable='company_name')
    row['variable_value'] = company_name

    return 'OK'

In general, you should have only one round trip per user interaction. It’s difficult to catch them all, it would be easier if the IDE showed them all, but I hope this helps.

Also, not performance related, but you should use alert instead of anvil.js.window.alert, because it looks much better (in my opinion).

Thank so much @stefano.menci, @hugetim and @p.colbert! I’m sure I can shave some time out, but I’m wondering about a more glaring issue, my client-side “Deepdive_Product_Generator” task, which seems to be the main culprit.

LOGIC:
I’ve got 15 boxes: ‘product_1_avatar_1… avatar_2’ etc. If the box contains text, than that box name is carried through the function, and executes the server launch function that then calls this background function, like below.

Is this the right and fastest way to do it to avoid a timeout?

Client Side

 for i in range(1, 6):
        # Get the product name and url from the textboxes
        product_name_input = getattr(self, f"product_{i}_name_input").text
       
        if product_name_input:
             anvil.server.call(f"launch_draft_deepdive_product_{i}_generator", self.user_table, company_name, product_name_input, product_url_input)

Then, on the Server Side:

def launch_draft_deepdive_product_1_generator(user_table, company_name, product_name, product_url):
    anvil.server.launch_background_task('deepdive_draft_product_1_generator', user_table, company_name, product_name, product_url)

@anvil.server.background_task
def deepdive_draft_product_1_generator(user_table,company_name,product_name,product_url):
#### A BUNCH OF WORK, then when it's done...

Do I need:

task = anvil.server.launch_background_task('deepdive_draft_product_1_generator'...
return task_get.id()       

Is this the right way to do it? I just want to call the task and move on, I don’t want to wait for it to return some info.

If you are getting a server side timeout, it is coming from the server function, not the background task. Since your server function is not doing anything other than launching the background task, look at how much data you are passing between the client and the server function. Too much will cause the timeout.

Can you pass less?

1 Like

You have a loop on the client that launches a bunch of backgrounds tasks.

Again, every launch requires the client to contact the server, so you have a bunch of slow round trips.

Also, instead of launching a burst of backgrounds tasks, it is usually (not always) better to launch one background task, then execute the loop in it. The result is going to be different, because the tasks will run sequentially rather than in parallel, which is what you want if they are CPU intensive or on the Anvil server. If they are not CPU intensive, or they are not on the Anvil server and other conditions are met, then it could make sense to run them in parallel.

1 - Just to be clear, when I hit my “Go Get All Assets” button, do have 30seconds for that single function to finish, or 30 seconds for all functions executed on the client side (and server side, outside of any background tasks), to finish?

2 - Do I need to get the server side ‘launcher function’ returned? Or do I need to just call it? 30seconds for Point A, or Point B?

Heres my (terrible) sketch.

Thanks! I want them to run in parallel because my user is sitting there waiting for it to finish. It’s running to OpenAI, so I need it to execute as fast as possible because right now, in parallel, it take 4 minutes for the first 4 tasks to run.

  • anvil.server.launch_background_task launches the task and immediately returns. The task can run longer than 30 seconds.

  • anvil.server.call runs the function, waits for the result, and crashes if it takes longer than 30 seconds.

Usually after launching a background task you have a timer on the client that every few seconds checks the task object to see if it’s finished, or, better, calls a server function that checks a row in a data table to see the status of the task. The task, while it runs, updates the status either in the task object, or, better, on a data table row.

If your background tasks spend most of the time waiting for an external service, then it does make sense to run them in parallel.

Still, launching background tasks from the client, just like updating table rows, is never a good idea.

The right way is to use the client to manage the UI, and run on the server any logic that requires contacting the server.

So, after any user interaction with the form, if you don’t need to contact the server at all, then you should run that part of the logic on the client. But, as soon as you need the server, to launch a task, to read a row from a table, to send an email, etc., then the form should call one and only one server function, passing all the data the function may need. Then the server function will do what it needs to do. This shaves 1/n% of the time from a client function, that is if you read/modify 5 rows and launch 5 tasks, you speed up the client code by 1/(5+5+5) = 93%.

2 Likes

Update: To second what jshaffstall said, I do think passing user_table is probably causing the timeout. I don’t see what else it could be. My guess is that you’re sending a huge amount of data to the server via server.call which then has to wait until it’s finished sending that same data to the background task via server.launch_background_task to return.

Also be sure to follow stefano.menci’s advice, but I don’t think that will solve the timeout issue.

1 Like

I’ve improved my code significantly (and taken all of your advice), and am still getting server call timeout errors. I was on a paid ‘Professional’ plan, but was so frustrated I’ve upped my account to ‘Business Plan’ (paying now $500cad per month), and it’s still happening.

Literally every time I launch this now, I’m getting server timeout errors, when previously my whole program was working perfectly and I never received a timeout error…ever.

Considering this was working on our previous beta launches over the past 3 weeks, the only thing I can think of is something changed on Anvil’s server side, and they somehow limited my account to 30seconds?

I’m losing real money from this timeout error.

1 Like

The first answer from @p.colbert suggested sprinkling print statements to see which line is too slow.

Did you find out?

Knowing what’s slow would help focusing on the right target, rather than on generic performance improvements.

If printing to the app log from multiple background tasks is confusing, you could add a Log table and add a row with time stamp and a comment to that table.

So far you haven’t, as far as I can tell, identified which exact server call is causing the timeout error. You’ve identified it as launch_draft_deepdive_product_1_generator but you call that function 6 times from the client. Is it the first time? The second? Third, etc?

You need to debug this thing to work out what’s causing the issue. Put print statements before and after the server call for that server function so you know exactly which iteration is causing the issue. Then dive into that iteration and work out how much information is being passed in. What happens if you comment out the call to start the background task from that function? Does the timeout still happen?

Without more information all you’re going to get from us is (admittedly good) general advice. If you continue to follow that general advice without nailing down exactly what’s causing your specific error, you’re going to continue to be frustrated.

Thanks @stefano.menci and @jshaffstall. Yes, I followed @p.colbert’s advice for timestamps (I’m in EST, Canada, so it’s sending it to Europe). See below.

One thing I’ve noticed is that when I call a function using a variable in a loop, it times out, but sometimes when I call it with hardcorded variables, it doesn’t.
For example:

anvil.server.call(f’launch_draft_deepdive_product_{i}_generator’, company_name) TIMES OUT, YET…

anvil.server.call(f’launch_draft_deepdive_product_1_generator’, company_name) WORKS FINE

Regarding timestamps. Until I get it operational (again), I’m running 4 basic tasks:

  1. Company Summary
  2. Brand Tone
  3. Product_1_Generator
  4. Generate Avatar for Product_1

Here’s all my timestamps.

  • ‘Get Assets’ Button Clicked by user: 2023-12-23 23:43:49
  • Server Side : Launch Company Summary received: 2023-12-24 04:43:59
  • Server Side : Launch Company Summary Function called: 2023-12-24 04:43:59
  • Client Side - Company Summary Function sent: 2023-12-23 23:44:00
  • Server Side: Launch Brand Tone function received: 2023-12-24 04:44:18
  • Server Side: Launch Brand Tone Background task called: 2023-12-24 04:44:18
  • Client Side: Launch Brand Tone function sent: 2023-12-23 23:44:19
  • Client Side Launching function: launch_draft_deepdive_product_1_generator
  • Server Side Product 1 Launcher has been received: 2023-12-24 04:44:41
  • Server Side Product 1 Background task request has been sent2023-12-24 04:44:41
  • The Client side launcher function for draft_product_1_generator has been sent: 2023-12-23 23:44:42

anvil.server.TimeoutError: Server code took too long* at Workspace_1, line 398
–>>> anvil.server.call('launch_avatar_generator’, j, product_num, company_name, product_name, avatar_input, self.user_email, self.active_workspace_name)

Note that I’m mid-way through trouble shooting, but here’s my code.

CLIENT SIDE

def go_get_all_assets_click(self, **event_args):
    # Launch server tasks and timers
     self.launch_server_tasks(company_name, company_url)

def launch_server_tasks(self, company_name, company_url):
  # Call server functions with the necessary parameters
    anvil.server.call('launch_draft_company_summary', company_name, company_url, self.user_email, self.active_workspace_name)
    timestamp_company = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Client Side - Company Summary Function sent: {timestamp_company}")
    self.undertaken_tasks.append('company_profile')

    anvil.server.call('launch_brand_tone_research', company_url, self.user_email, self.active_workspace_name)
    timestamp_brand = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Client Side: Launch Brand Tone function sent: {timestamp_brand}")
    self.undertaken_tasks.append('brand_tone')
    
     # Now call the product generator method
    self.launch_draft_deepdive_product_generators(company_name)
    timestamp_product_generator = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Client Side: Product Generator client-side function sent: {timestamp_product_generator}")

def launch_draft_deepdive_product_generators(self, company_name):
    # Check and launch for Product 1
    product_name_1 = self.product_1_name_input.text
    product_url_1 = self.product_1_url_input.text
    
    if product_name_1:
        self.undertaken_tasks.append('product_1_latest')
        print("Client Side Launching function: launch_draft_deepdive_product_1_generator")
        anvil.server.call('launch_draft_deepdive_product_1_generator', company_name, product_name_1, product_url_1, self.user_email, self.active_workspace_name)
        timestamp_product_1 = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f'The Client side launcher function for draft_product_1_generator has been sent: {timestamp_product_1}')
        self.launch_avatar_generators(1, company_name, product_name_1)
        self.task_check_timer_product_1.enabled = True

### CHEATING A BIT HERE UNTIL IT FUNCTIONS
def launch_avatar_generators(self, product_num, company_name, product_name):
    j = 1
    avatar_input = getattr(self, f"avatar_{j}_product_{product_num}_input").text
    anvil.server.call('launch_avatar_generator', j, product_num, company_name, product_name, avatar_input, self.user_email, self.active_workspace_name)

Now, here’s my SERVER SIDE (each one launches a background task):

@anvil.server.callable
def launch_draft_company_summary(company_name, company_url, user_email, workspace_column):
    # Directly launch the background task with essential parameters
    timestamp_draft_company_received = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side : Launch Company Summary received: {timestamp_draft_company_received} ")
    anvil.server.launch_background_task('draft_company_summary_scraper', company_name, company_url, user_email, workspace_column)
    timestamp_draft_company_summary = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side : Launch Company Summary Function called: {timestamp_draft_company_summary} ")

@anvil.server.callable
def launch_brand_tone_research(brand_tone_url, user_email, workspace_column):
    timestamp_brand_received = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side: Launch Brand Tone function received: {timestamp_brand_received }")
    # Directly launch the background task
    anvil.server.launch_background_task('brand_tone_research', brand_tone_url, user_email, workspace_column)
    timestamp_brand_launch = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side: Launch Brand Tone Background task called: {timestamp_brand_launch}")

@anvil.server.callable
def launch_draft_deepdive_product_1_generator(company_name, product_name, product_url, user_email, workspace_column):
    timestamp_product_1 = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side Product 1 Launcher has been received: {timestamp_product_1}")
    anvil.server.launch_background_task('deepdive_draft_product_1_generator', company_name, product_name, product_url, user_email, workspace_column)
    timestamp_product_1_launch = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side Product 1 Background task request has been sent{timestamp_product_1_launch}")

@anvil.server.callable
def launch_avatar_generator(avatar_num, product_num, company_name, product_name, avatar_preview, user_email, workspace_column):
    timestamp_draft_deepdive_avatar_received = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side - The Avatar launcher task request has been received: {timestamp_draft_deepdive_avatar_received}")  
  # Immediately launch the background task with necessary parameters
    anvil.server.launch_background_task('draft_deepdive_avatar_generator', avatar_num, product_num, company_name, product_name, avatar_preview, user_email, workspace_column)
    timestamp_draft_deepdive_avatar_generator = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"Server Side - The Avatar Background task request has been sent: {timestamp_draft_deepdive_avatar_generator}")

Does anything jump out at you? It seems it’s constantly failing in the brand tone research… However, if I comment out the first task “company summary”, brand_tone works, but then ‘launch_draft_deepdive_product_1_generator’ times out…

I just don’t understand because my app was working before in production with a beta group- with 20 of these tasks being called at once, and I NEVER received a timeout- ever. Now it’s not working at all. It’s totally bizarre.

I’m thinking of creating a new QA post and putting up a $50 bounty, haha!

I’d really appreciate any insight you have.

Merry Christmas gents!

*EDIT: It seems that if I send everything in one big server all, then launch all the background tasks from that one server-side launcher function, it works much faster and doesn’t time out.

I still see the control passing from client to server, back and forth multiple times.

The right way to do this would be to have the click event create a dictionary with all the info collected from the form, then call one server function that does the job. There should be one function on the client and one server callable on the server. Then everything should happen on the server side, and all the calls should be normal python calls, not anvil.server.calls.

From the log I see that the client tried to call a server function, but the server function is never executed. I’ve never seen a function that is not executed and times out (but it doesn’t mean it doesn’t happen).

I don’t see any information about the background tasks. How are they doing after the timeout error? Are they still running?

You could keep it a little cleaner with a simple logging function similar to this (untested):

def print_t(txt):
    print(f'{datetime.datetime.now():%Y-%m-%d %H:%M:%S} {txt}')
1 Like
  1. That’s what I’ve been saying
  2. It’s faster, but if then you add back what you have removed, you risk to hit the wall again. If this is the case, then you could try to:
  • The client called a server callable
  • The server callable launches a launcher background task, and immediately returns the control to the client, so no timeout
  • The launcher background task launches all the other background tasks
2 Likes

Great advice- thank you @stefano.menci. One question though- I’ve tried to launch background tasks from other background tasks, and it doesn’t work.

Is the below code the best way to execute it?

CLIENT SIDE

def go_get_all_assets_click(self, **event_args):
    all_info = run_loops_to_collect(textbox_1 & textbox_2, etc)
    self.master_launcher(all_info)

def master_launcher(all_info):
  anvil.server.call('master_launcher_on_server', all_info)

SERVER SIDE

@anvil.server.callable
def master_launcher_on_server(all_info):
    self.master_launcher_quick_return_and_launch_all_tasks(all_info)
    # returns confirmation quickly

def master_launcher_quick_return_and_launch_all_tasks(all_info):
   some_info = all_info
   anvil.server.launch_background_task('company_summary', some_info)
   anvil.server.launch_background_task('brand_tone_research', some_info)
   anvil.server.launch_background_task('product_1_generator', some_info)
   anvil.server.launch_background_task('avatar_generator', some_info)

I really appreciate your help. Is there any other notes you’d suggest?

Cheers!

My background tasks are usually CPU intensive, so I never have multiple tasks running in parallel.

If a background task can’t launch another background task, and the logic required to decide whether to launch a task is too time expensive, then you could launch all the tasks and let each task decide whether to exit immediately or continue with its job.


I don’t know what your run_loops function is intended to do. In my mind I would simply do:

def go_get_all_assets_click(self, **event_args):
    all_info = {
        'name': self.name.text,
        'age': self.age.text,
        [...]
    }
    anvil.server.call('master_launcher_on_server', all_info)

Try:

@anvil.server.callable
def master_launcher_on_server(all_info):
    anvil.server.launch_background_task(‘ master_launcher_quick_return_and_launch_all_tasks’,all_info)
    return True

#don’t forget the decorator
@anvil.server.background_task
def master_launcher_quick_return_and_launch_all_tasks(all_info):
   some_info = all_info
1 Like