-1

I have a problem with promised array: I'm calling inside my switch a function which is loading an array from API Example:

let sorted = []
let limit = 10
async function loadPage(slcLimit) {
    let i
    let filter = document.getElementById("show-filter").value

    if (sorted.length == 0) {
        switch (filter) {
            case "1":
                let realWoolPromiseAsc = await Promise.resolve(realWool(pagingPouches))
                    .then((realWoolArr) => {
                        sorted = realWoolArr.sort((a, b) => parseFloat(a.realWool) - parseFloat(b.realWool));
                        return sorted
                    })
                break;
            case "2":
                let realWoolPromiseDesc = await Promise.resolve(realWool(pagingPouches))
                    .then((realWoolArr) => {
                        sorted = realWoolArr.sort((a, b) => parseFloat(b.realWool) - parseFloat(a.realWool));
                        return sorted
                    })
                break;
        }
    }


        for (i = startingLimit; i < (startingLimit + limit); i++) {
            console.log(sorted[i].ID + " - " + sorted[i].price)
        }
}


Real wool function

window.realWool = async function realWool(pouchArr) {
    let divider = 1000000000000000000n;
    let realWoolArray = []
    pouchArr.forEach(async pouch => {
        let availAmount = await pouchContract.amountAvailable(pouch.pouchID)
        availAmount = Number(BigInt(availAmount.toString()) * 100n / divider) / 100
        availAmount = Math.floor(availAmount * 100) / 100
        let rWool = pouch.pouchWool - availAmount
        realWoolArray.push({pouchID: pouch.pouchID, realWool: rWool, pouchTime: pouch.pouchTime})
        //document.getElementById("lockedText" + pouch.pouchID).innerHTML = "Real WOOL: <span class='black'>" + rWool + "</span>"
    });
    return realWoolArray
}

I need to use the sorting array inside my for loop but I'm getting undefined. I understand I need to use await or a then block I just have no clue how to use that.

Thank you!

I've used a timeout, but it is not optimal since sometimes the function just return an array of 5 objects and sometimes a hundreds of objects (depends on filters)



setTimeout(() => {
        for (i = startingLimit; i < (startingLimit + limit); i++) {
            console.log(sorted[i].ID + " - " + sorted[i].price)
        }
}, 5000);


  • It's going to be hard to help without a [mre]. Can you update your question to include one? See [ask]. – jsejcksn Nov 26 '22 at 14:28
  • I've edited it. Hope this is better. – nodejs_newbie Nov 26 '22 at 15:10
  • Your `loadPage` function does not return anything, so it resolves to `undefined`. Is that intended? – Heiko Theißen Nov 26 '22 at 15:25
  • @HeikoTheißen this is still not the whole function. It is working with the timeout but it is not the best solution. – nodejs_newbie Nov 26 '22 at 15:44
  • Why do you use `Promise.resolve`? What is `realWool`, is it even asynchronous? Does it return a promise? – Bergi Nov 26 '22 at 16:18
  • What's he point of the `let realWoolPromiseAsc = ` (and `…Desc`) declarations? You're not using those variables anywhere. – Bergi Nov 26 '22 at 16:19
  • @Bergi It literally doesnt matter. This is not a full function this is just a small block of code. It is returning a promise and it has to be asynchronous. My problem is: I don't want to use TIMEOUT what should I use instead of timeout? Thats my question and I don't know the answer. – nodejs_newbie Nov 26 '22 at 17:25
  • @nodejs_newbie You don't need a timeout or anything. Your code uses an `await`, which means it pauses in the `switch`/`case` and executes the `for` loop only afterwards. If that doesn't work, there is a problem with your `realWool` function. – Bergi Nov 26 '22 at 19:36
  • @Bergi then please explain why it is working with the timeout and without timeout im getting `undefined` -> the realwool function is getting data from Ethereum contract and just returning an array of objects. – nodejs_newbie Nov 27 '22 at 00:32
  • @nodejs_newbie If it doesn't work, show us the `realWool` function implementation. I'm pretty sure it doesn't return an array. – Bergi Nov 27 '22 at 00:48
  • @Bergi it is working with timeout so imo it is returning an array. I will post the realWool function to the first question. – nodejs_newbie Nov 27 '22 at 01:45
  • 1
    Ah, it's returning (a promise for) an array, but that array is empty (until later, which is why you see the full array in the `setTimeout` callback). The problem is that [you cannot use `forEach` with async functions](https://stackoverflow.com/q/37576685/1048572) – Bergi Nov 27 '22 at 02:00

3 Answers3

1

You need to return from the then block.

And spell length correctly.

You do not need to set the module scoped sorting in the then block either.

     let promisedArray = await Promise.resolve(myFunction())
     .then((realArray) => {
         sorting = realArray.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
        
         return sorting // <-- return to awaited value
      })

      for (let i = 0; i < promisedArray.length; i++) { <-- check spelling
         console.log(promisedArray[i]) //returns the value I need
        }

You do not need await Promise.resolve either, just await realWool.

Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
  • 1
    It will never get to that point, because the sort routine in the then block will throw. – Steven Spungin Nov 26 '22 at 14:41
  • Unfortuntately it is still not working for me and I'm getting an undefined. I will edit the question with the real block of code maybe it will be more helpful. This was a dummy block of code - one of the reasons why my spelling was bad. – nodejs_newbie Nov 26 '22 at 15:07
  • @nodejs_newbie Put a log statement in after your api call returns, in the then statement, to ensure you are getting your data. – Steven Spungin Nov 26 '22 at 15:17
  • @StevenSpungin if I use the timeout Im getting the data. – nodejs_newbie Nov 26 '22 at 15:43
1

Your problem is here: pouchArr.forEach(async pouch => {...}) this is a fire and forget situation. forEach will start all the tasks at once and then as soon as all have started, forEach is done and your code goes to the next command return realWoolArray before even the first entry has been added.

Then in loadPage your sort will sort a (currently) empty array, ... and when all your synchronous code is done, and the Promises start to resolve, your code pushes the items all the way into the sort array.

window.realWool = async function realWool(pouchArr) {
    const divider = 1000000000000000000n;
    return Promise.all(
      pouchArr.map(async (pouch) => {
        let availAmount = await pouchContract.amountAvailable(pouch.pouchID)
        availAmount = Number(BigInt(availAmount.toString()) * 100n / divider) / 100;

        return {
          pouchID: pouch.pouchID, 
          realWool: pouch.pouchWool - availAmount, 
          pouchTime: pouch.pouchTime
        }
      })
    );
}
Thomas
  • 11,958
  • 1
  • 14
  • 23
0

The array sorted that you use in the loadPage function is a global variable, which means that all invocations of loadPage (I assume that each incoming request causes one invocation) will access the same array concurrently. This cannot lead to correct (or even predictable) results.

Put the let sorted = [] inside the function body to make it local. And the function must return something, for example return realWoolPromiseAsc.

Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31
  • This has nothing to do with my problem at all. Again: My block of code is fully WORKING I just don't want to use TIMEOUT and I assuem I could use AWAIT or something similar --- I just don't know how to do it. I dont want to use timeout because: sometimes my api returns 5 objects, sometimes 5000... – nodejs_newbie Nov 26 '22 at 16:23