I am trying to work out an average value for a collection in my database using Mongoose and Express. I want to use this value in the render for the "calculator" page, hence it being inside a post for the page "calculator".
However, when I do the model.find({}) function, the value is gone after the function is executed and it does not send the modified value to the page.
I have tried putting it inside a function and calling it to assign to the value but I have a feeling I may be returning the value wrong.
If anybody has any understanding of how to do this please let me know.
Below is the code for the JS file:
// Calculation POST Handler:
router.post("/calculator", ensureAuthenticated, (req, res) => {
/*BEGINNING OF CALCULATION METHOD*/
const ethAddress = req.body.ethereumAddress;
const KG_CO2_PER_GAS = 0.0001809589427;
const CO2T_COST = 40; // The cost of Co2 by the Tonne(1000kg) for electricity, found from page 26 of https://www.lse.ac.uk/GranthamInstitute/wp-content/uploads/2019/05/GRI_POLICY-REPORT_How-to-price-carbon-to-reach-net-zero-emissions-in-the-UK.pdf
var hourlyFlightCo2 = 250; // 250kg Co2 per flight.
var addressChecked = ethAddress;
var kilosOfCO2Created = 0;
var userTransactions = []; // Stores the data retrieved from Etherscan API.
var totalGasValueForAverage = 0;
var totalNumofTransactionsForAverage = 0;
let totalAverageGasValue = 0;
var totalValue = 0;
var numOfTransactions = 0;
var priceOfOffset = 0;
var userID = req.user._id;
var flightHours = 0;
var totalAverageGasValueToDisplay = 0;
/* PART 1: For getting data, using node-fetch .json to convert the data into an array of objects*/
fetch(
`https://api.etherscan.io/api?module=account&action=txlist&address=${ethAddress}&startblock=0&endblock=99999999&sort=asc&apikey=${APIKey}`
)
.then((data) => {
return data.json();
})
.then((retrievedData) => {
// Extract just the results from the Javascript object for a Javascript object of just the transactions.
userTransactions = retrievedData.result;
// console.log(userTransactions)
/* PART 2: Create a loop that takes all gasUsed values from all previous transactions and adds them together for a number to be used in the calculation of emissions. */
for (const value of userTransactions) {
// Save transaction to db.
let dbGasUsed = value.gasUsed;
let dbEthereumAddress = ethAddress;
let dbTransactionHash = value.transactionHash;
// Find all transactions by hash, if they are not already in the database then add them to the database.
Transaction.findOne({ transactionHash: dbTransactionHash }).then(
(transaction) => {
if (transaction) {
// Check if the user already exists
} else {
const newTransaction = new Transaction({
userID,
dbGasUsed,
dbEthereumAddress,
dbTransactionHash,
});
newTransaction.save();
}
}
);
let currentValue = Number(value.gasUsed); // Calculate the total amount of gas used by the user.
totalValue = totalValue currentValue;
numOfTransactions ;
}
// Calculate average of stored transactions gasUsed
Transaction.find({})
.then((transactions) => {
for (const value of transactions) {
totalNumofTransactionsForAverage ;
totalGasValueForAverage = (totalGasValueForAverage value.dbGasUsed);
}
// TODO: NEED TO FIGURE OUT HOW TO STORE THIS VARIABLE FOR USE ON PAGE.
totalAverageGasValueToDisplay = totalGasValueForAverage/totalNumofTransactionsForAverage;
});
/* PART 3: Calculate values that will be useful for the user to see, such as the total number of transactions tracked, the size of their carbon footprint and the cost to offset it.*/
kilosOfCO2Created = Math.round(totalValue * KG_CO2_PER_GAS);
priceOfOffset = (kilosOfCO2Created / 1000) * CO2T_COST; // Calculate cost: ((user emissions(kg) / 1000(kg)) * cost by the tonne (£40))
flightHours = kilosOfCO2Created / hourlyFlightCo2;
res.render("calculator.ejs", {
numOfTransactions: numOfTransactions,
totalValue: totalValue,
kilosOfCO2Created: kilosOfCO2Created,
priceOfOffset: priceOfOffset,
addressChecked: addressChecked,
flightHours: flightHours,
totalAverageGasValue: totalAverageGasValue,
totalAverageGasValueToDisplay: totalAverageGasValueToDisplay
});
});
});
The part I want to use in the render is the value "totalAverageGasValueToDisplay", found being assigned inside the transaction.find({})
CodePudding user response:
save function is an asynchronous, for using this you need to insert await before it like below:
Transaction.findOne({ transactionHash:
dbTransactionHash }).then(async
(transaction) => {
if (transaction) {
// Check if the user already
exists
} else {
const newTransaction = new
Transaction({
userID,
dbGasUsed,
dbEthereumAddress,
dbTransactionHash,
});
await newTransaction.save();
}
}
);
CodePudding user response:
Transaction.find({})
is executed synchronously after the Transaction.findOne({transactionHash: dbTransactionHash})
statements have been issued for all userTransactions
, but before any of them has finished.
More generally, part 2 of your code contains many asynchronous statements that must be finished before part 3 can be executed. To achieve this, part 3 must come in a .then
function after part 2.
/* PART 2: ... */
var findStatements = [];
for (const value of userTransactions) {
...
findStatements.push(
Transaction.findOne({transactionHash: dbTransactionHash}).then(
(transaction) => {
...
return newTransaction.save().then(function() {
let currentValue = Number(value.gasUsed);
totalValue = totalValue currentValue;
numOfTransactions ;
});
}
)
);
}
Promise.all(findStatements).then(function() {
// Calculate average of stored transactions gasUsed
return Transaction.find({}).then(...);
}).then(function() {
/* PART 3: ... */
...
});