javascript - How to set the call back in WEB3
问题描述
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".
Question
- 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);
});
解决方案
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);
});
- 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 ingetInvestorsAndBalances
- 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);
});
});
- I'm using arrow functions instead of function declarations for more consise code
- 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
- I'm declaring variable
dataSample
with const; you didn't declare it at all, neitheri
orelement
, which made them global variables – this is a bad practice - I'm using template strings to construct the data string for XLS export
- I've fixed formatting issues in your data string – it was not generating valid JSON because you got confused with the quotes and commas
- 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) - I've removed the console statements, assuming you just used these for debugging
推荐阅读
- javascript - lodash 在对象数组中搜索文本
- visual-studio - 在 Visual Studio 中,环境变量在哪里定义?
- ms-access - 如何在访问数据库报告上创建关闭按钮
- javascript - 在 Axios 中结束请求
- javascript - 如何在 nuxtjs 中使用 /foo-:bar 路由?
- javascript - 添加到 Google 日历呈现链接不显示用户的当地时间
- mongodb - mongodb shell命令上传视频文件
- mysql - 在 MySQL 中从长到宽重塑数据
- java - Android工作室,任务':app:packageDebug'的执行失败
- python - FieldError at / 无法将关键字“title_icontains”解析为字段。选项有:完成、创建、解密、id、标题、用户、user_id