List(search_iterator) does not run synchronously

What I’m trying to do:
Transform a Search Iterator into a list of something else AND THEN use that list for other stuff with that list. However, when I try to transform that into a list, the operations seems to run non_blocking and continues as if it was asyncronous.

What I’ve tried and what’s not working:
The following code will try to exemplify what I try to do and what happens

Code Sample:


@classmethod # included in another file, inside AnotherClass
def from_search(cls, search):
    rows = []
    if search:
        for row in search:
            rows.append(cls(row))
    print('converted')
    return rows

# inside the main file, main class
def main(self):
    self._results = None
    server_results = anvil.server.call('getResults')
    # server_results is a search iterator with more than 0 results
    # Line below will try to transform into a list of AnotherClass
    self._results = AnotherClass.from_search(server_results)
    print(self._results) # This will print None before  'converted' is printed

Any idea why this is like this? And how to force the last line to wait for the entire operation running in from_search?

I use accelerated tables (in case this makes a difference).

That looks like it might be a bug! If you can provide a cut-down clone link with an example to demonstrate the issue, we’ll take a look.

1 Like

I apparently can’t…

I tried to make a MVE, but in that example worked perfectly. I then tried to add routing to see if it was something about it, but still worked fine.

In my app, however, still results in error, but it envolves a lot of things… It envolves a 1st-party dependencies, anvil_extras, routing, context manager, etc.
It also results in error only when acessing a page directly instead of acessing “/” first and then navigating to the page.

When I try to access a certain page directly it gives me this error:

TypeError: 'NoneType' object is not iterable
at Components.Franquia.FranquiaSelectorComponent, line 112
called from app/routing/router/_route.py:149
called from app/routing/router/_router/client.py:224
called from app/routing/router/_router/client.py:224
called from app/routing/router/_router/client.py:143
called from app/routing/router/_router/client.py:230
called from app/routing/router/_router/client.py:271
called from app/routing/router/_router/client.py:274
called from app/routing/router/_router/client.py:309
called from Main, line 35
called from Main, line 35
called from Main, line 38

This is Main, line 28 to 38:
Main is the startup module. Line 38 calls the method main inside of it and line 35 is calling the launch() from routing dependency.

The component that is in error is in the form that loads for this specific page.

I can still provide a clone link… but will need a lot of dependencies to work, since the app is not that small, but the error occours exactly in the moment the app is run, so i’s easy to reproduce.

@gabriel.duro.s

I think the way you are using non-blocking is probably the issue
You have an on_result handler and also call await_result

It’s probably that your on_result handler is firing after await_result completes, at which point your list of items is still None

so after calling await_result you should probably do something like


rv = self.some_async_call.await_result()
self.some_result_handler(rv) # force the value here

Hum…
I did see some weird things on the await_result and the on_result methos.

I thought that when I run the await_result it would then wait, run the on_result and THEN go back to the code. In fact, I didn’t even remember that the await_result returned the value to begin with…

So… if have:

def on_result_method(self, result):
  self.variable = do_operation(result)

def main_method(self):
  self.async_call = call_async(get_result)
  self.async_call.on_result(self.on_result_method)

def get_result_awaiting(self):
  if self.async_call.status == 'PENDING': # ignoring the possibility of None
    self.on_result_method(self.async_call.await_result)
  return self.variable

If you call main_method and before on_result_method is naturally called, call get_result_awaiting, this would make the on_result_method be called twice, but would ensure that self.variable is the correct value (with all the things the on_result_method could need to do with the result and considering that doing things TWICE wouldn’t broke anything) after waiting for the result?

Edit: It DID call twice, but it also did work! So, as expected, it was something I was doing wrong, but I was never going to find out by myself.

I’m just wondering now if this really is the best approach or if I’m missing something else. The cenario is that I load a few things (mostly tied to the logged user) that the user probably won’t immediately need, but certainly eventually will, so I anticipate it asyncronously to make a better user experience. Usually the await_result won’t be used, but when navigating directly to the page that it needs, this was occouring.

Yeah that sounds sensible, assuming that do_operation isn’t a lot of work

But if it is a lot of work
then another option would be to do the following

class MyClass:

    def get_something_on_server_and_do_something_expensive(self):
        result = anvil.server.call_s("get_something")
        self.variable = self.do_operation(result)

    def main_method(self):
        self.async_call = call_async(self.get_something_on_server_and_do_something_expensive)

    def get_result_awaiting(self):
        if self.async_call.status == 'PENDING': # ignoring the possibility of None
            self.async_call.await_result()
        return self.variable


1 Like

Hum… I see. Makes a lot of sense, since the server call and the setting would be both in the same async function, so waiting for the result means waiting for the call AND the processing, which is more what I thought of in the beginning and wouldn’t require to execute the same method twice.

Thanks for the help!

1 Like