1

I have two functions in a blockchain contract to get the investors and funds for an address and tried to save that in Excel.

1st Function To get the list of investors:

getInvestorsList()

2nd Function This will take the investor address as input and returns the investor's address and the funds saved for that address:

getInvestorsAndBalances(address investorAddress)

I was able to get the list of investors and the funds sponsored by them using the functions getInvestorsList and getInvestorsAndBalances.

The below snippet, which convert the data to Excel has to executed only when the function getInvestorsAndBalances executes completely for all investors. But this code is executed even before the call to the contract completes. Hence I am not getting the values from the blockchain to the below snippet.

How to make the below code wait for the successful completion of the function getInvestorsAndBalances?

dataSample = dataSample + "]";
console.log("dataSample: " + dataSample);
//var dataSample = [{"address": "abc","balance": "21.22"}]; 
const xls = new XlsExport(dataSample,  'Example WB');  
xls.exportToXLS('export.xls')  

Complete Code

crowdSaleContractObj.getInvestorsList(function(error, result){
    if(!error)
        {    
            for (i=0; i < result.length; i++) {  
      
                crowdSaleContractObj.getInvestorsAndBalances(result[i],function(error, result1){
                console.log(i);
                
                if(!error)
                    {      
                        console.log(i + " - Address : " + result1[0]+ ",  Balance : " + result1[1]);
                        element = " {\"address\": " + result1[0] + ",balance:" + result1[1] + "},";
                        console.log("element: " + element);
                        dataSample = dataSample + element;
                    }
                else
                    console.error(error);
                });   
            }
            
            dataSample = dataSample + "]";
            console.log("dataSample: " + dataSample);
            //var dataSample = [{"address": "abc","balance": "21.22"}]; 
            const xls = new XlsExport(dataSample,  'Example WB');  
            xls.exportToXLS('export.xls')  
            
        }
    else
        console.error(error);
});  
TylerH
  • 20,799
  • 66
  • 75
  • 101
iappmaker
  • 2,945
  • 9
  • 35
  • 76
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Patrick Hund Dec 23 '18 at 21:18
  • The above link answers are all related to Ajax. I do not need any Ajax related stuff.. This is pure jquery and web3 – iappmaker Dec 24 '18 at 02:08
  • Yes, but the basic principle is the same: getInvestorsAndBalances is an asynchronous operation with a callback function, so you're going to have to use promises, combined with Promise.all – Patrick Hund Dec 24 '18 at 07:08
  • Could you please point me the exact example that I need to follow – iappmaker Dec 24 '18 at 08:45
  • Ajax stands for Asynchronous JavaScript. jQuery and Web3 are written in JavaScript, so you are, in fact, using Ajax, too. – TylerH Feb 08 '23 at 15:29

1 Answers1

2

Here's a version of your code that works:

crowdSaleContractObj.getInvestorsList(function(error, results) {
  if (!error) {
    const promises = results.map(function(result, i) {
      return new Promise((resolve, reject) => {
        crowdSaleContractObj.getInvestorsAndBalances(result[i], function(
          error,
          result1
        ) {
          console.log(i);

          if (!error) {
            console.log(
              i + " - Address : " + result1[0] + ",  Balance : " + result1[1]
            );
            resolve(result1);
          } else {
            console.error(error);
            reject(error);
          }
        });
      });
    });

    Promise.all(promises)
      .then(function(results1) {
        results1.forEach(r => {
          element = ' {"address": ' + r[0] + ",balance:" + r[1] + "},";
          console.log("element: " + element);
          dataSample = dataSample + element;
        });
        dataSample = dataSample + "]";
        console.log("dataSample: " + dataSample);
        //var dataSample = [{"address": "abc","balance": "21.22"}];
        const xls = new XlsExport(dataSample, "Example WB");
        xls.exportToXLS("export.xls");
      })
      .catch(function(error) {
        console.error(error);
      });
  } else console.error(error);
});
  1. Instead of a for loop, I'm using Array.map to transform the results into an array of promises which resolve with the value returned to the callback in getInvestorsAndBalances
  2. In the next step, I'm calling Promise.all on this array of promises; this causes the code in the then method to be executed only after all the investors and balances have been retrieved asynchronously

Improving the Code

I've taken the liberty to refactor your code a bit to use modern language features and fix some mistakes:

crowdSaleContractObj.getInvestorsList((error, results) => {
  if (error) {
    console.error(error);
    return;
  }
  const promises = results.map(
    (result, i) =>
      new Promise((resolve, reject) => {
        crowdSaleContractObj.getInvestorsAndBalances(
          result[i],
          (error, result1) => {
            if (error) {
              reject(error);
              return;
            }
            resolve(result1);
          }
        );
      })
  );

  Promise.all(promises)
    .then(results1 => {
      const dataSample = `[${results1
        .map(r => `{"address": "${r[0]}", "balance": ${r[1]}}`)
        .join(", ")}]`;
      const xls = new XlsExport(dataSample, "Example WB");
      xls.exportToXLS("export.xls");
    })
    .catch(function(error) {
      return console.error(error);
    });
});
  1. I'm using arrow functions instead of function declarations for more consise code
  2. I'm using the early return pattern (check if there's an error, then immediately return from the function) to avoid too much deep nesting of if/else statements
  3. I'm declaring variable dataSample with const; you didn't declare it at all, neither i or element, which made them global variables – this is a bad practice
  4. I'm using template strings to construct the data string for XLS export
  5. I've fixed formatting issues in your data string – it was not generating valid JSON because you got confused with the quotes and commas
  6. I'm using Array.map to create the dataSample string, combined with Array.join to avoid the problem of having a trailing comma at the end of the data (which caused invalid JSON format)
  7. I've removed the console statements, assuming you just used these for debugging
Patrick Hund
  • 19,163
  • 11
  • 66
  • 95