1

In my contract there are 2 functions, the first one returns an array size and other one returns data from the array with index passed as a parameter.

In my web3 script I'm trying to read array size and when done, perform loop, put data into the array, sort data from highest to lowest value and display it on the screen.

The problem is data is asynchronous, for now it displays data before finishing the loop. I need to wait until the loop is complete. How can I change this code to make it working?

The following code is inside async function:

const total = await promisify(cb => platform.getArraySize(cb));
items = [];
for(let i = 0; i < total; i++)
{
    // wait??
    platform.getData(i, function(error, result)
    {
        items[i] = [result[0].toNumber(), result[1].toNumber()];
        items = items.sort(function(a, b)
        {
            return b[1] - a[1]
        });
    });
}
for(let i = 0; i < items.length; i++)
{
    $("#container").append("<div>a: " + items[i][0] + ", b: " + items[i][1] + "</div>");
}
0

A callback can be adapted into a promise, then the await keyword can be used to pause the loop until the callback cause the promise to complete, for example:

for(let i = 0; i < total; i++)
{
    const {error, result} = await new Promise((resolve) => platform.getData(i, (e, r) => {e,r});
    items[i] = [result[0].toNumber(), result[1].toNumber()];
    items = items.sort(function(a, b)
    {
        return b[1] - a[1]
    });
}

Since this syntax is verbose, there are libraries (such as bluebird) to do this for you.

const getData = Promise.promisify(platform.getData)
for(let i = 0; i < total; i++)
{
    const result = await getData(i)
    items[i] = [result[0].toNumber(), result[1].toNumber()];
    items = items.sort(function(a, b)
    {
        return b[1] - a[1]
    });
}
0

I think the confusion is mostly around how JavaScript promises work (which is common!) YDKJS is a good resource. Another thing to note is that web3.js returns promises by default, so we don't need to "promisify" them.

I'd go about it like this:

  1. Get array size (platform.getArraySize)
  2. Create an array of promises (platform.getData) for each index position.
  3. Execute all of the promises in the array at the same time (and wait for all of them to complete)
  4. Sort the results
  5. Display the results

Code (untested):

 // 1. get length
 const total = await platform.getArraySize();

// 2. create array of promises. each promise will get the data, "then" format the results
const promiseArray = [];
for (let i = 0; i < total; i++) {
    promiseArray.push(
        platform.getData(i).then(result => {
            return [result[0].toNumber(), result[1].toNumber()];
        })
    );
}

// 3. execute all promises in parallel, return after *all* promises are complete
const items = await Promise.all(promiseArray);

// 4. sort
items = items.sort(function(a, b) {
    return b[1] - a[1];
});

// 5. add to DOM
for(let i = 0; i < items.length; i++)
{
    $("#container").append("<div>a: " + items[i][0] + ", b: " + items[i][1] + "</div>");
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.