Issues with Array Variable - javascript

app.get("/indsalesx/:store/:mm", (req, res) => {
connect();
let ddd = [];
let staffarray = [{}];
let store = req.params.store;
let mm = req.params.mm;
const SP = mongoose.model(`sales${store}`, Sales);
let num = stafflist[store].length - 1;
for (i = 0; i <= num; i++) {
let staffname = stafflist[store][i];
let calc = 0;
SP.find(
{ v_salesperson: stafflist[store][i], v_month: mm },
"v_amount",
(err, doc) => {
let t = doc.length - 1;
doc.map((res) => {
calc = calc + res.v_amount;
});
ddd.name = staffname;
ddd.amount = calc;
staffarray.push(ddd);
}
);
}
console.log(staffarray);
});
The issue I have is: Why is staffarray returning an empty array? staffarray was declared as an empty array of objects, and in a loop function, objects were pushed to to array. But when I console.log(staffarray), it returns the empty array of objects declared initially.
Any help on what to do?

When using find(), you can use 2 approaches.
Pass a callback function
await the function to execute and return the results.
It appears that you used the first approach which means that you are passing a callback into the find() method which handles the result once received.
The console.log() code line will execute before the result will return since it's the next line to execute after the for loop.
So, let's go through what it happening here:
Javascript is executing the find() code line.
That line of code is being placed in the web API which are the pieces of the browser in which concurrency kicks in and makes the call to the server for us.
The console.log() line is being executed with an empty array (since the results haven't been received yet.
After some time, results came back and the callback is being set in the callback queue.
The JS event loop takes the callback from the callback queue and executes it.
This is part of the javascript event loop. you could read more about this here
Mongoose documentation: Model.find()

you can use for of with async/await instead of for
app.get("/indsalesx/:store/:mm", async(req, res) => {
connect();
let ddd = [];
let staffarray = [{}];
let store = req.params.store;
let mm = req.params.mm;
const SP = mongoose.model(`sales${store}`, Sales);
let num = stafflist[store].length - 1;
var list = Array.from(Array(num).keys());
for (let i of list) {
let staffname = stafflist[store][i];
let calc = 0;
let doc = await SP.find(
{ v_salesperson: stafflist[store][i], v_month: mm },
"v_amount"
);
let t = doc.length - 1;
doc.map((res) => {
calc = calc + res.v_amount;
});
ddd.name = staffname;
ddd.amount = calc;
staffarray.push(ddd);
}
console.log(staffarray);
});

I have been able to solve it, all I needed was proper structuring with the async and await statements.
app.get("/indsalesx/:store/:mm", async (req, res) => {
connect();
let ddd = {};
let staffarray = [];
let store = req.params.store;
let mm = req.params.mm;
const SP = mongoose.model(`sales${store}`, Sales);
let num = stafflist[store].length - 1;
for (i = 0; i <= num; i++) {
let staffname = stafflist[store][i];
let calc = 0;
await SP.find(
{ v_salesperson: stafflist[store][i], v_month: mm },
"v_amount",
(err, doc) => {
let t = doc.length - 1;
doc.map((res) => {
calc = calc + res.v_amount;
});
staffarray.push({ name: staffname, amount: calc });
}
);
}
console.log(staffarray);
res.send({ data: staffarray });
});

Related

How to write array data to json?

How to write the data that the loop passes to the array "eventsPolygon", to json. This array returns 4 args. With this method, I get the error "TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined"
async function main() {
console.log("Start checking rewards")
const currentBlockNumberPolygon = await maticProvider.getBlockNumber() - 1
const currentBlockNumberBsc = await bscProvider.getBlockNumber() - 1
const oldestBlockNumberPolygon = 22939848
const oldestBlockNumberBsc = 13763979
const eventFilterPolygon = Missions.filters.RewardToPay()
const eventFilterBsc = Rewards.filters.RewardPayed()
let eventsPolygon = []
let eventsBsc = []
for (let i = oldestBlockNumberPolygon; i < currentBlockNumberPolygon - 10000; i += 10000) {
const eventsLoop = await Missions.queryFilter(eventFilterPolygon, i, i + 10000)
eventsPolygon = eventsPolygon.concat(eventsLoop)
const jsonData = JSON.stringify(eventsPolygon);
fs.writeFile('eventsBsc.json', jsonData.finished)
console.log(i)
}
//for(let i = oldestBlockNumberBsc; i < currentBlockNumberBsc-10000; i+=10000) {
//const eventsLoop = await Rewards.queryFilter(eventFilterBsc, i, i+10000)
// eventsBsc = eventsBsc.concat(eventsLoop)
//console.log(i)
//}
console.log('a')
}
JSON.stringify returns a string representation of your JSON. So you cannot do this:
fs.writeFile('eventsBsc.json', jsonData.finished)
Simply write jsonData to the file:
await fs.writeFile('eventsBsc.json', jsonData);
Be aware that this function is async. You need to await it

Trying to use replace() method on files by passing the string not working - JS

I'm trying to replace a string, but when i try to do it on a file, it does not work. Or only the last file will work.
Main Code (inside a class method):
const funcs = fs.readdirSync(path.join(__dirname, "../funcs")).filter(file => file.endsWith('.js'));
this.functions = this.code.split("$");
const functions = this.functions
for (const func of funcs) {
for (let x = functions.length - 1; x > 0; x--) {
let i = 0;
const res = await require(`../funcs/${func}`)(client, this.code.toString(), this._author);
console.log(x);
console.log(res);
console.log(functions);
return res;
i++;
}
}
ping.js:
const ping = (client, code, author) => {
const result = code.split("$[ping]").join(client.ws.ping)
return result;
}
module.exports = ping;
messageAuthorTag.js:
const Util = require('../utils/util');
const messageAuthorTag = (client, code, author) => {
if (code === null) return;
const res = code.split("$[message.author.tag]").join(`${author.tag}`);
return res;
}
module.exports = messageAuthorTag;
Using it : "ping: $[ping] author tag: $[message.author.tag]"
Output:
ping: $[ping] author tag: Example#0001
Your issue is that you return res in the middle of a for loop. This means it won't do anything else in the function and will skip the rest of the loops (like running the other functions!)
As an additional note, you could also remove the let i = 0 and i++ since that doesn't seem to be used for anything.

Calling promise-driven functions regularly with setInterval and operating over its results

My goal is to make a video playlist generator that runs at specific times, along with a running clock and many other things that run periodically, but I'm stuck at the part where it generates the playlist at the times I define.
I'm using FileHound to walk a folder, which works whenever I call the find() method, but as it's Promise driven, its results aren't readily available to be used by the following console.log(), for example. Same thing happens with MediaInfo(), but this time I was able to work around it with async/await, which as far as I know is the actual way to use a Promise based function.
Now, as far as I can understand, the .each() and .then() method chaining are the ways to use the results from a Promise driven function, but that would quickly result in code repetition for every time I want to do the same thing in different places.
All being said, I think I'm off my track by very far, and despite my efforts I can't seem to find a clear way to achieve what I want, so I'm asking for help. This is the code I have so far, and I'm using the following npm packages:
node-mediainfo
filehound
moment
const MediaInfo = require("node-mediainfo");
const Path = require("path");
const FileHound = require("filehound");
const Moment = require("moment");
const START_TIME = Moment();
const END_TIME = null;
const VIDEO_PATH = "videos";
let files = FileHound.create()
.path(VIDEO_PATH)
.depth(0);
let playlist = [];
let start_time = START_TIME.add(10, "seconds");
const ArrayShuffle = (array) => {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
};
const MakePlaylist = async (file) => {
let fileinfo = await MediaInfo(Path.resolve(file));
let duration = fileinfo.media.track[0].Duration;
let title = fileinfo.media.track[0].Title || Path.basename(file, Path.extname(file));
let artist = fileinfo.media.track[0].Performer || null;
playlist.push({
path : Path.resolve(file),
title : title,
artist : artist,
duration: duration
});
};
console.log(start_time.toLocaleString());
/* Main Loop */
setInterval(() => {
if (Moment().isSame(start_time)) {
console.log("First Run");
files.find().each(MakePlaylist).then(ArrayShuffle);
console.log(playlist);
}
if (Moment().isSame(Moment(start_time).add(30, "seconds"))) {
console.log("Second Run");
playlist = [];
files.find().each(MakePlaylist).then(ArrayShuffle);
console.log(playlist);
}
}, 1);
I would be tempted to put your logic into an async function, making use of await. and then simply recall your function later, instead of trying to do it in setTimeout
Assuming files.find() returns a promise....
const GeneratePlaylist = async () => {
var files = await files.find();
var playlist = [];
for(var i=0;i<files.length;i++){
playlist.push(await MakePlaylist(files[i]));
}
ArrayShuffle(playlist);
return playlist;
}
Then you can use that from another async function, and you can recall it 1ms later with setTimeout (Note, you should make MakePlaylist return the new item, not push to a global array):
const doMyThing = async (){
if (Moment().isSame(start_time)) {
console.log("First Run");
playlist = await GeneratePlaylist();
console.log(playlist);
}
if (Moment().isSame(Moment(start_time).add(30, "seconds"))) {
console.log("Second Run");
playlist = [];
playlist = await GeneratePlaylist();
console.log(playlist);
}
setTimeout(doMyThing,1);
}
Below is a working example, where I've just faked up some of your functionality using Promises to simulate asynchronous work like finding files and loading the media info:
const ArrayShuffle = (array) => {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
};
const MakePlaylist = async (file) => {
let fileinfo = await new Promise(resolve => setTimeout(() => resolve({media:{track:[{Duration:1,Title:"Title",Performer:"Foo"}]}}),1000));
let duration = fileinfo.media.track[0].Duration;
let title = fileinfo.media.track[0].Title || Path.basename(file, Path.extname(file));
let artist = fileinfo.media.track[0].Performer || null;
return{
path : file,
title : title,
artist : artist,
duration: duration
};
};
const findFileMockup = () => {
return new Promise(resolve => setTimeout(() => {
resolve(["file1.txt","file2.txt","file3.txt"]);
},500));
}
const GeneratePlaylist = async () => {
var files = await findFileMockup(); // await files.find() in your code
var playlist = [];
for(var i=0;i<files.length;i++){
playlist.push(await MakePlaylist(files[i]));
}
ArrayShuffle(playlist);
return playlist;
}
const test = async () => {
var playlist = await GeneratePlaylist();
console.log(playlist);
}
test();

How to wait for iteration to complete before returning

I am trying to loop through an Array of JSON objects (var requestArray = req.body;, specifically requestArray['filter']), persisting each object into a database. After each persistence, I pull the last persisted data table, add it to an array let responseDataArray = []; in responseDataArray.push(result);. This array is then returned as a request response.
app.post('/sound', function (req, res) {
var requestArray = req.body;
let responseDataArray = [];
for (var i = 0; i < requestArray['filter'].length; i++) {
if (i > 3)
break;
var revEl = requestArray['filter'][i];
// console.log('GUID >>> ' + i + ' : ' + revEl['_revEntityGUID'] + ' >>> ' + JSON.stringify(revEl));
persistSingleItemPromise(revEl).then(function (result) {
responseDataArray.push(result);
console.log(JSON.stringify(responseDataArray));
});
}
console.log((responseDataArray));
res.send(responseDataArray);
});
The problem is in the for loop. It delays, and I only return an empty array responseDataArray = [] since it returns before the iteration completes.
I have tried using a Promose persistSingleItemPromise:
let persistSingleItemPromise = function (revData) {
return new Promise(function (resolve, reject) {
revPersSaveRevEntity.revPersSaveRevEntity(revData, function (result) {
resolve(result);
});
});
};
This doesn't help. How can I resolve this?
Thank you all in advance.
I was thinking of something like this.
Didn't test it please let me know if it works ;-)
Keep in mind, that your callback needs the async prefix too.
const resultPromise = requestArray['filter'].reduce( async ( accPromise, revEl ) => {
const acc = await accPromise
const result = await persistSingleItemPromise(revEl)
acc.push( result )
return result
}, Promise.resolve( [] ) )
const responseDataArray = await resultPromise
You could use Promise.all and store the promises. Then, wait for all of them to resolve
Like
app.post("/sound", function(req, res) {
var requestArray = req.body;
let responsePromises = [];
for (var i = 0; i < requestArray["filter"].length; i++) {
if (i > 3) break;
var revEl = requestArray["filter"][i];
// console.log('GUID >>> ' + i + ' : ' + revEl['_revEntityGUID'] + ' >>> ' + JSON.stringify(revEl));
responsePromises.push(persistSingleItemPromise(revEl));
}
Promise.all(responsePromises).then(result => res.send(result));
});
An example simulation here
const promises = [];
for (let i = 1; i < 4; i++) {
promises.push(new Promise(resolve => {
// Simulate asynchronous request
setTimeout(() => {
resolve("Resolved " + i);
}, 100 * i);
}));
}
// Notice how the result takes some time.
// It's basically waiting for all the promises to resolve
Promise.all(promises).then(results => console.log(results));
I think you should add all your promises from "persistSingleItemPromise" to an array and perform a Promise.All(list).then() on them and await the result before returning.

Asynch fetch in For-loop - access to result (finished) variable

I'm new to JS asynchronous and I have a question about: how to start working on created array only if all queries are done. I fetch pages in for loop. That's my code:
var allOrgReposData = [];
var repoContributorsUrls = [];
for (var i=1; i <= orgPageIterations; i++) {
var orgReposUrl = 'https://api.github.com/orgs/angular/repos?page='+i;
fetch(orgReposUrl)
.then(response => response.json())
.then(orgReposData => {
allOrgReposData = allOrgReposData.concat(orgReposData);
console.log(allOrgReposData);
})
}
As You can see the allOrgReposData array is created on for loop, but If I try to do something on this array, script do It on every iteration so my operations are multipicated instead execution single time for exapmle (30 item on page): 30; 60; 90; 120; 150; 171 = 621 instead 171.
Is It possible to "wait" for finish fetching and get access to array without this. "multiplication"?
Greetings!
You can use Promise.all which will wait until all promises are complete:
var allOrgReposData = [];
var repoContributorsUrls = [];
var promises = [];
let orgPageIterations = 1;
for (var i = 1; i <= orgPageIterations; i++) {
let orgReposUrl = 'https://api.github.com/orgs/angular/repos?page=' + i;
promises.push(fetch(orgReposUrl).then(response => response.json()));
}
Promise.all(promises)
.then(data => {
allOrgReposData = data;
console.log(allOrgReposData);
})
.catch(err => console.error(err));
Please note that I've also changed var orgReposUrl to let orgReposUrl to make us of block scoping.
You could keep track of the number of calls you did with a variable :
var allOrgReposData = [];
var repoContributorsUrls = [];
var callSuccess = 1; //Variable keeping track of your ajax calls
for (var i=1; i <= orgPageIterations; i++) {
var orgReposUrl = 'https://api.github.com/orgs/angular/repos?page='+i;
fetch(orgReposUrl)
.then(response => response.json())
.then(orgReposData => {
allOrgReposData = allOrgReposData.concat(orgReposData);
console.log(allOrgReposData);
callSuccess++; //Increment your var for each call
if(callSuccess == orgPageIterations){ //If every call has already been made, then continue
//DO YOUR THING HERE
}
})
}

Categories

Resources