Asynchronous Blocks within Loops

loops
asynchronous
recursive
hints
realtimedb

#1

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


Problems with For loop!
Problem repeating item in list viewer
Delete Rows from Airtable
Get data(street name) from geocoder google api
Speech recognizer - I want it to wait for an answer
Speech recognizer - I want it to wait for an answer
Get List Index from Airtable (Row Number)
Firebase - Values updated automatically reverted
#2

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


#3

@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


#4

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


#5

:ok_hand::ok_hand::smiley:


#6

@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


#7

@Rob_Schoemaker,

Great idea! Thanks.

-Mark


#8

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.


#9

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.


#10

Sid,

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

-Mark


#11

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


#12

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


#13

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


#14

Thanks Mark ! this was very helpful :slight_smile:


#15

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


#16

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


#17

Yeah, I did thanks