Asynchronous Blocks within Loops

We recently had a bug reported by a user who was seeing strange behaviors within the “then do” part of a RealtimeDB.Save block which was within a loop block. The strange part was that even though each execution of the RealtimeDB.Save block was called with the current value of the loop variable (or something based on the current value) the blocks within the “then do” part always just saw the last value of the loop variable!

This is a well known issue when using asynchronous function blocks (like RealtimeDB.Save) within a loop. Asynchronous functions are ones which run independently of the other blocks that they are contained within. They exist because you usually do not want functions like RealtimeDB.Save, which might take a long time to execute, to block the execution of other code. RealtimeDB.Save can take a long time to execute because it sends and receives data over the web.

In the long run, we need to make it easier to deal with scenarios like this. In the short run, here’s a workaround. The basic idea is to create your own recursive looping function block. Recursive functions are ones that call themselves within their definition. I have created a project which shows a (hopefully) simple example of this, looping over RealtimeDB.Save. That project can be copied by following this link.

The app has two buttons which when pressed will loop from 1 to 10, storing the loop variable values into a RealtimeDB and then displaying the loop variable values after each Save. The first button uses a normal loop and show the behavior that you are complaining about. The second button uses the approach that I am suggesting as a workaround and shows all the loop variable values.

There’s also a third button that you can press to get the value stored at a particular key (which you can enter via a Text Input) in the RealtimeDB. That functionality is just provided for convenience and testing.

The key part of the project is seen in these blocks:
screen shot 2018-06-28 at 11 16 57 am

You’ll need to adapt those blocks for your own use.

I hope this helps.

Happy Thunking!

-Mark

9 Likes

Hello Mark,

In your block I see recursion, but in my simple project recursion results in the appearance of a white screen on iOS and Android.

https://x.thunkable.com/projects/5b353cf538d4a3763090d893/project/properties/designer/

recursion

Is recursion working in Thunkable X?

Regards
Alex

1 Like

@actech

Александр,

When doing recursion you need some condition which eventually will end the recursion. Otherwise you end up with an infinite loop or a stack overflow (which is probably what is happening in your case). You’ll notice in my example that I only make the recursive call to AsynchLoop when currentVal <= endVal. That provides a way for the recursion to end when (i.e. when currentVal > endVal).

-Mark

2 Likes

Oh, thanks Mark!

When someone takes too long on the road, he sees the mountains well in the distance and does not notice the stones under his feet, as in my case!

Alex

6 Likes

:ok_hand::ok_hand::smiley:

2 Likes

@Mark Thanks for your explanation! Maybe it is an idea to add to the documentation of each block if the block is blocking/non-blocking (synchronous/asynchronous)?

(I guess that it can be seen from the structure of a block (if it contains a “when … done” part it is asynchrounous?) but I am not sure about that)

Thanks!
Rob

1 Like

@Rob_Schoemaker,

Great idea! Thanks.

-Mark

1 Like

Thanks Mark, I was running into this exact problem and solved it using your described approach! Are there any plans to add callbacks (or something equivalent) that could be used to trigger another set of blocks when an asynchonous block completes?

My use case involves uploading a set of photos to Cloudinary and storing the resulting mediaURLs into firebase. I have included a screenshot of the working block design with the hope it may help others.

1 Like

Most of the blocks working asynchronously already have this mechanism. For example, “photoDB call Upload” or “RealtimeDB call Save” have a “then do” part, which is called after the action is completed. But you are right, some of the blocks do not have this mechanism, which complicates the development and leads to the need to use a timer.

Sid,

The "then do" slot is a callback slot. Blocks placed there are run when the asynchronous block completes.

-Mark

1 Like

Ah, right – but it can act up when used in loops? (hence this discussion thread)

1 Like

As I mentioned in my original post:

We are, in fact, working on some changes which might should help alleviate this problem. In the meantime, please use recursion if you need to serialize your asynchronous blocks (i.e. ensure that they run after one another) within a loop.

-Mark

1 Like

Got it – thanks for the quick replies @mark and @actech!

1 Like

Thanks Mark ! this was very helpful :slight_smile:

2 Likes

Hi Mark,

I’m unable to access the project through the link in your post. Please can you reconfirm the link and the permission of the project, and share an updated link.

Thanks

1 Like

Hi @Nenye, did you hit the “click to remix” link at the top of the project?

2 Likes

Yeah, I did thanks

1 Like

Hi all

I have similar issues with a web API call that I would use to send customized emails.
The idea is that first I get the list via an API call from Google sheets, then I use its content the customize the email address, and the body of the email (replacing names, dates, links etc.).
Once the body is updated I would like to send the emails one by one to the different addresses with different content.
I started to use a loop which did not work then I tried to use the same solution which is mentioned above but I was not able to solve my issue, and I hope that I missed something which you can catch.

This is how my script looks like:

And this is the simple google script on the other end:

When I use these blocks I send all the emails (72 emails) with the same email body, address, and subject and all of them go with the details of the first element in my list.

Just for your reference, I created a different block that sends 3 different emails and it is working perfectly:


Do you have any idea how could I fix it?

Your AsyncGmail function is going to run right after the SendEmail button is clicked. It’s not going to wait for the first API call to complete.

So since you’re depending on variable values set within the “then do” section of the first API call, you’re going to have to move that function call to the “then do” section.

Sorry, I just realized that the first picture has been cut too early.
I use the first call of asyncGmail to initiate the process.
So this is how it looks like:

So at the bottom, you can see that I call the recursion within the “then do” section of the email sending API post.

Please let me know If I still need to change something.