No Async with forItems & forEachItems!

Hello there :raised_hand_with_fingers_splayed:

One big problem I’ve had for a long time, when running one of the above functions on an array (regular and repeater data) sometimes I need to wait until a promise is resolved before continue to the next item, use case is if the array holds IDs that you need to query, or an upload button that’s awaiting the URL of the uploaded item, it’s just insane, this where I always stuck in each project and can’t find any workarounds, the code is just ignoring the async/await as if it wasn’t there.

Any workarounds guys? Any suggestions?
Thanks in advance.

I believe the issue in such a case is that you might be hitting the limit of web/data requests (about 250 per minute). You’re right in stating that the await should actually, you know, wait until the request is finished, but the system limits are probably causing some the awaits to be “ignored” (or not resolved).

I always try to defer requests till needed, but I realize that this isn’t always possible.

I’m going to send this on to the dev team to see if they have any ideas.

Thank you Yisrael , as far as I can remember, async/await don’t work in loops with callbacks. I did find a workaround to force it to wait until all promises are resolved, but I just can’t understand why :thinking:.

@ahmadnasriya Ah, I was just starting to write that you can do Promise.all(), but that doesn’t solve all cases. And yes, callbacks are a problem, but I think that I once succeeded in getting callbacks working.

@yisrael-wix yes, it wasn’t possible in my case since I was using the repeater’s forItems loop to start an upload and add the uploaded files into an array, so they need upload one at a time.

As a workaround, I created a while loop with a variable to invalidate it once the result is returned by checking if the array length is equal to the data length in the repeater before moving to the next part.

@ahmadnasriya Could you set up an example that I can share with the devs? Thanks

@yisrael-wix Of course, example about the problem or the solution?

@ahmadnasriya I would like to show the devs what the problem is, so an example of the problem. Although, an example of a workaround would be great for anyone who needs this.

@yisrael-wix My scenario is a repeater where you can add/remove items from it, each item has an upload button, on submitting, a validation check is called to check if all the buttons have pending files and that they’re valid before calling another function that will start uploading file individually and add the uploaded URLs to an array to insert in a collection in a later step.

The solution is already included :ok_hand:

let uploadedFiles = [];
let saved = false;
let finished = false;

for (let i = 0; i < $w('#rep1').data.length; i++) {
   let id = $w('#rep1').data[i]._id;

   await $w('#rep1').forItems([id], async ($item, itemData, index) => {
       await $item('#upload').startUpload().then((uploadedFile) => {
          uploadedFiles.push(uploadedFile.url);
      }
   }
   let data = $w('#rep1').data
   while (uploadedFiles.length === data.length && submitting && !saved && !finished) {
      // Invalidate the loop
      saved = true;
      submitting = false;
      
      // Move to the next part
   }  
}   

@ahmadnasriya I want to show the devs what doesn’t work.

@yisrael-wix Here’s the problem:

let items = [];

$w('#rep').forEachItem(async($item) => { // 3 items in the array
    let item = // await Promise
    items.push(item);
    console.log(item)
})

console.log(items)

The results are returned in this sequence:

[] // Empty array - from line 9
{} // Object from line 6
{} // Object from line 6
{} // Object from line 6

Instead of:

{} // Object from line 6
{} // Object from line 6
{} // Object from line 6
[ {}, {}, {} ] // An array of objects - from line 9

Adding an await keyword before the loop doesn’t change anything too:

await $w('#rep').forEachItem(async($item) => {})

@ahmadnasriya You’re right, an await before the loop won’t do anything.

@yisrael-wix Yes, I understand, thought about giving the problem and the solution separately.

Yes I know, I just did it for the sake of experiment.

@ahmadnasriya I didn’t read the entire thread, so maybe you referred to it somewhere, but why wouldn’t you do something like this in your case? :

let uploadingButtons = [];
$w("#repeater1").onItemReady ($item, itemData, index) => {
uploadingButtons.push($item("upladButton"));
})
//then when you want to start loading the files, let's say on button click:
Promise.all(uploadingButtons.map(e => e.startUpload()))
.then(r => {
let urls = r.map(e => e.url);
});

Or maybe that not what you meant (sorry I read fast)

@jonatandor35 Thanks for answering the question :blush:

Does saving the repeated item selector in an array lets me perform actions on that item as a repeated item from outside the repeater scoop? I haven’t thought about that before.

@ahmadnasriya Yes, you can work on repeater items outside the repeater.onItemReady() scope, as long as you have a reference to the repeater elements.

@jonatandor35 Brilliant! Thanks for the awesome tip, very much appreciated :slightly_smiling_face:

You’re welcome