Nested async await combined with loops - node.js

Edit:
Here is my full code that throws no errors:
router.get('/:id/all', async (req, res, next) => {
let id = req.params.id;
let t;
let d;
let o;
let p;
let data = [];
let entry = {};
try {
let rowConfig = await methods.runQuery('SELECT SystemID, Begin, End, Power FROM Configuration WHERE ID = ?', id);
p = rowConfig[0].Power;
let begin = rowConfig[0].Begin;
let end = rowConfig[0].End;
let counter = [Var1, Var2, Var3];
let result = await methods.runQuery('SELECT * FROM Table1 WHERE ID = ?', id);
result.forEach(async function(row) {
let resultT;
if (!row.EventStop) {
t = row.EventBegin - begin;
resultT = await methods.runQuery('SELECT EventBegin, EventStop FROM Table1 WHERE ID = ? AND RootID IN (4, 5) AND IsValid = TRUE AND EventBegin < ?', [id, row.EventBegin]);
}
else {
t = row.EventStop - begin;
resultT = await methods.runQuery('SELECT EventBegin, EventStop FROM Table1 WHERE ID = ? AND RootID IN (4, 5) AND IsValid = TRUE AND EventBegin < ?', [id, row.EventStop]);
}
console.log(t);
d = 0;
resultT.forEach(function(row) {
let dt = row.EventStop - row.EventBegin;
d = d + dt;
});
o = t - d;
console.log(o);
entry = {'1': t, '2':o};
data.push({entry});
});
}
catch (error) {
res.status(500).send({ error: true, message: 'Error' });
}
return res.send({ data: data });
});
But the data array that is sent remains emtpy! So i looked in my console and two stand out.
The values of tand o are wrong
They get logged in the console when the get method already has finsihed. So when the data array is sent, obviously there are no entry in there.
The order in which the code should be excuted:
For each row of Table1 i want to have t and o, so it should be:
forEach loop (row1)
-> if/else -> calculate t
-> get resultT
-> forEach loop (row1)
-> calculate d
-> forEach loop (row2)
-> calculate d
-> ...
-> calculate o
-> push entry to data
-> forEach loop (row2)
-> ...
I am working with node and express for creating a Rest API. I am currently struggling because of the asynchronous behaviour of node. This is what I am trying to achieve:
In a get method, I want to collect all the data of table from my database.
router.get('/:id/all', async (req, res, next) => {
...
try {
let result = await methods.runQuery('SELECT SystemID, Begin, End, Power FROM Configuration WHERE ID = ?', id);
The runQuery method looks like this :
module.exports.runQuery = function(sql, parameter){
return new Promise((resolve, reject) => {
dbConn.query(sql, parameter, function (error, result, fields) {
if (error) reject(error);
else {
resolve(result);
}
});
});
}
This works so far. Now my problems start.
For each row of this table I want to do something.
Inside this for loop I need to do calculations that depend on each other.
}

As I have mentioned in the comment, await won't work in forEach loop. Try for...of loop instead.
Example:
for (let row of result) {
// await will work here
}

for/forEach loops in javascript are not promise/async/await aware so you will need to use Promise.all instead of for loop. Basically something like
Promise.all(result.map(result => doSomethingAsync()))
Make sure there are not large data in result as you will be spinning those many promises in parallel. Better will be to use promise batch or streaming.

Related

Efficient way to filter an array | Discord.js v13

So I am trying to make a leaderboard command specific to the server the command is run in, however I have found that it is very inefficient and takes a very long time.
Here is my code below:
let m = await msg.channel.send("Loading... :loading:");
let top = Object.entries(lb).sort((a, b) => a[1] - b[1]).reverse();
let count = 0, topu = 0, tope = [];
while (count < 3) {
if (topu > (top.length - 1)) {
while (count < 3) {
tope.push("No user found");
count++;
};
break;
};
try {
await msg.guild.members.fetch(top[topu][0]);
} catch {
topu++;
continue;
};
tope.push(`<#${top[topu][0]}> - ${top[topu][1]} points`);
count++;
topu++;
};
let e = new Discord.MessageEmbed().setDescription(`🥇 ${tope[0]}\n\n🥈 ${tope[1]}\n\n🥉 ${tope[2]}`);
m.edit({ content: `Top 3 players in **${msg.guild.name}**`, embeds: [e] });
Does anyone happen to know a better algorithm to do something like this?

discord js how to sort json file money by descending

I try to sort a money in my json file but every time i get "money.slice is not a function"
can someone help with this? "money.sort((a,b)=>{b.money - a.money});" dont work to
My JSON file:
{"2858236346346":{"name":"user1","money":100}}, {"346342356347436":{"name":"user2","money":1000}}
The Code I have trouble with:
execute(client, message, args) {
let rawdata = fs.readFileSync('./money.json');
let money = JSON.parse(rawdata);
console.log(money);
const res = money.slice().sort((a, b) => b.money - a.money);
console.log(res);
let embed = new Discord.MessageEmbed()
.setTitle("Najbogatsi Gracze")
//if there are no results
if (res.length === 0) {
embed.setColor("RED");
embed.setThumbnail("https://i.imgur.com/pWHpi7G.png");
embed.addField("Nie znaleziono danych", "Wpisz na czacie $daily aby zdobyć pieniądze!")
} else if (res.length < 10) {
//less than 10 results
embed.setColor("BLURPLE");
embed.setThumbnail("https://i.imgur.com/pWHpi7G.png");
for (i = 0; i < res.length; i++) {
let member = message.guild.members.cache.get(res[i].id) || "Brak danych"
if (member === "Brak danych") {
embed.addField(`${i + 1}. ${member}`, `**Kasa:**: ${res[i].money}zł`);
} else {
embed.addField(`${i + 1}. ${member.user.username}`, `**Kasa:**: ${res[i].money}zł`);
}
...
Nevermind I just figured it out we must use loop and define new "const" in this case "res = [];" so then we can push a new json and then sort it
let money = JSON.parse(fs.readFileSync("./money.json", "utf8"));
const res = [];
for(let userID in money){
res.push({"id": userID, "money": money[userID].money })
res.sort((a, b) => Object.values(b)[1] - Object.values(a)[1])
console.log(money, res);
}

NodeJS: API not returning response after loop execution is completed although it returns inside the loop

In my api creation there is one strange issue I come across that it does not return the final output after successful execution of the nested forEach loop.
But it consoles the result inside the loop execution. So, what is the thing that I am missing to get the response ?
repository.ts
export class ItemInventoryRepository {
public async getItemsData(itemId) {
try {
let mainItemList = [];
var subList;
let mainQuery = `SELECT table1.id as colOneId,
FROM table1
WHERE table1.item_id = ${pool.escape(itemId)} AND option_id = 1`
let optOneRecords = await pool.query(mainQuery);
if(optOneRecords.length > 0) {
optOneRecords.forEach(async element => {
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery);
await mainItemList.push({
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
});
console.log("outSideLoopData :: ", mainItemList)
}
return mainItemList;
} catch(error) {
throw error
}
}
}
In the below attached terminal screen shot the outSideLoopData consoles first and then inSideLoopData consoles.
Postman response where getting the actual response is returning empty array.
controller.ts
public async getItemById(req: Request, res: Response, next: NextFunction) {
try{
let repository = new ItemInventoryRepository;
let result = await repository.getItemsData(req.params.id);
res.status(200).json({data: result})
} catch(error) {
next(error)
}
}
---- await dosen't work in forEach loop ------
optOneRecords.forEach(async element => {
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery); // await dosen't work in forEach
await mainItemList.push({ loop
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
});
Pleae use for loop
for(const element of optOneRecords){
let id = element.colOneId
let subQuery = `SELECT table1.id as colTwoId,
FROM table1
LEFT JOIN table2 ON table1.id = table2.col_two_id
WHERE table1.item_id = ${pool.escape(itemId)}
AND option_id = 2
AND table2.col_one_id = ${id}`
subList = await pool.query(subQuery);
await mainItemList.push({ // await dosen't work in forEach loop
"colOneId": element.colOneId,
"subList": subList
})
console.log("inSideLoopData :: ", mainItemList)
}

Async/Await Node-Postgres Queries Within ForEach Loops

EDIT: I'm using node v8.0.0
I just started learning how to access SQL databases with node-postgres, and I'm having a little bit of trouble accessing multiple databases to collect the data in a work able format, particularly with executing multiple queries within forEach loops. After a few tries, I'm trying async/await, but I get the following error:
await client.connect()
^^^^^^
SyntaxError: Unexpected identifier
When I tried using a pool or calling .query sequentially, I would get something along the lines of
1
[]
could not connect to postgres Error: Connection terminated
Here is an abbreviated version of my code:
const { Client } = require('pg');
const moment = require('moment');
const _ = require('lodash');
const turf = require('#turf/turf');
const connString = // connection string
var collected = []
const CID = 300
const snaptimes = // array of times
var counter=0;
const client = new Client(connString);
function createArray(i,j) {
// return array of i arrays of length j
}
await client.connect()
snaptimes.forEach(function(snaptime){
var info = {}; // an object of objects
// get information at given snaptime from database 1
const query1 = // parametrized query selecting two columns from database 1
const result1 = await client.query(query1, [CID,snaptime]);
var x = result1.rows;
for (var i = 0; i < x.length; i++) {
// store data from database 1 into info
// each row is an object with two fields
}
// line up subjects on the hole
const query2 = // parametrized query grabbing JSON string from database 2
const result2 = await client.query(query2, [CID,snaptime]);
const raw = result2.rows[0].JSON_col;
const line = createArray(19,0); // an array of 19 empty arrays
for (var i = 0; i < raw.length; i++) {
// parse JSON object and record data into line
}
// begin to collect data
var n = 0;
var g = 0;
// walk down the line
for (var i = 18; i > 0; i--) {
// if no subjects are found at spot i, do nothing, except maybe update g
if ((line[i] === undefined || line[i].length == 0) && g == 0){
g = i;
} else if (line[i] !== undefined && line[i].length != 0) {
// collect data for each subject if subjects are found
line[i].forEach(function(subject){
const query 3 = // parametrized query grabbing data for each subject
const result3 = await client.query(query3,[CID,subject,snaptime]);
x = result3.rows;
const y = moment(x[0].end_time).diff(moment(snaptime),'minutes');
var yhat = 0;
// the summation over info depends on g
if (g===0){
for (var j = i; j <= 18; j++){
yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
}
} else {
for (var j = i; j <= 18; j++){
if (i<j && j<g+1) {
yhat = moment.duration(info[j].field2).add(yhat,'m').asMinutes();
} else {
yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
}
}
}
collected.push([y,yhat,n,i]);
});
}
n+=line[i].length;
g=0;
}
// really rough work-around I once used for printing results after a forEach of queries
counter++;
if (counter===snaptimes.length){
console.log(counter);
console.log(collected);
client.end();
}
});
The problem is caused by your forEach callback not being async:
snaptimes.forEach(function(snaptime){
should be:
snaptimes.forEach(async function (snaptime) {
for the await to be recognizable at all.
Keep in mind that an async function returns immediately and it returns a promise that gets eventually resolved by return statements of the async function (or rejected with uncaught exceptions raised inside the async function).
But also make sure your Node version supports async/await:
Since Node 7.6 it can be used with no --harmony flag.
In Node 7.x before 7.6 you have to use the --harmony flag.
It was not available in Node before 7.0.
See: http://node.green/#ES2017-features-async-functions
Also note that you can use await only inside of functions declared with the async keyword. If you want to use it in the top level of your script or module then you need to wrap it in an immediately invoked function expression:
// cannot use await here
(async () => {
// can use await here
})();
// cannot use await here
Example:
const f = () => new Promise(r => setTimeout(() => r('x'), 500));
let x = await f();
console.log(x);
prints:
$ node t1.js
/home/rsp/node/test/prom-async/t1.js:3
let x = await f();
^
SyntaxError: Unexpected identifier
but this:
const f = () => new Promise(r => setTimeout(() => r('x'), 500));
(async () => {
let x = await f();
console.log(x);
})();
prints:
$ node t2.js
x
after 0.5s delay, as expected.
On versions of Node that don't support async/await the first (incorrect) example will print:
$ ~/opt/node-v6.7.0/bin/node t1.js
/home/rsp/node/test/prom-async/t1.js:3
let x = await f();
^
SyntaxError: Unexpected identifier
and the second (correct) example will print a different error:
$ ~/opt/node-v6.7.0/bin/node t2.js
/home/rsp/node/test/prom-async/t2.js:3
(async () => {
^
SyntaxError: Unexpected token (
It's useful to know because Node versions that don't support async/await will not give you a meaningful error like "async/await not supported" or something like that, unfortunately.
Make sure that you should use async block outside like:
async function() {
return await Promise.resolve('')
}
And it is default supported after node 7.6.0. Before 7.6.0, you should use --harmony option to work for it.
node -v first to check your version.
First of all, you don't know enough about async-await just yet. don't worry, it's actually quite easy; but you need to read the documentation to be able to use that stuff.
More to the point, the problem with your code is that you can only await inside async functions; you're doing that outside of any function.
First of all, here's the solution that is closest to the code you wrote:
const { Client } = require('pg');
const moment = require('moment');
const _ = require('lodash');
const turf = require('#turf/turf');
const connString = // connection string
var collected = []
const CID = 300
const snaptimes = // array of times
var counter=0;
const client = new Client(connString);
function createArray(i,j) {
// return array of i arrays of length j
}
async function processSnaptime (snaptime) {
var info = {}; // an object of objects
// get information at given snaptime from database 1
const query1 = // parametrized query selecting two columns from database 1
const result1 = await client.query(query1, [CID,snaptime]);
var x = result1.rows;
for (var i = 0; i < x.length; i++) {
// store data from database 1 into info
// each row is an object with two fields
}
// line up subjects on the hole
const query2 = // parametrized query grabbing JSON string from database 2
const result2 = await client.query(query2, [CID,snaptime]);
const raw = result2.rows[0].JSON_col;
const line = createArray(19,0); // an array of 19 empty arrays
for (var i = 0; i < raw.length; i++) {
// parse JSON object and record data into line
}
// begin to collect data
var n = 0;
var g = 0;
// walk down the line
for (var i = 18; i > 0; i--) {
// if no subjects are found at spot i, do nothing, except maybe update g
if ((line[i] === undefined || line[i].length == 0) && g == 0){
g = i;
} else if (line[i] !== undefined && line[i].length != 0) {
// collect data for each subject if subjects are found
line[i].forEach(function(subject){
const query 3 = // parametrized query grabbing data for each subject
const result3 = await client.query(query3,[CID,subject,snaptime]);
x = result3.rows;
const y = moment(x[0].end_time).diff(moment(snaptime),'minutes');
var yhat = 0;
// the summation over info depends on g
if (g===0){
for (var j = i; j <= 18; j++){
yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
}
} else {
for (var j = i; j <= 18; j++){
if (i<j && j<g+1) {
yhat = moment.duration(info[j].field2).add(yhat,'m').asMinutes();
} else {
yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
}
}
}
collected.push([y,yhat,n,i]);
});
}
n+=line[i].length;
g=0;
}
// really rough work-around I once used for printing results after a forEach of queries
counter++;
if (counter===snaptimes.length){
console.log(counter);
console.log(collected);
}
}
async function run () {
for (let snaptime of snaptimes) {
await processSnaptime(snaptime);
}
}
/* to run all of them concurrently:
function run () {
let procs = [];
for (let snaptime of snaptimes) {
procs.push(processSnaptime(snaptime));
}
return Promise.all(procs);
}
*/
client.connect().then(run).then(() => client.end());
client.connect returns a promise and I use then to call run once it's resolved. When that part is over, client.end() can be called safely.
run is an async function, therefore it can use await to make the code more readable. The same goes for processSnaptime.
Of course I can't actually run your code, so I can only hope I didn't make any mistakes.

How to write async.series nested for loop

I have this code that works:
require! [async]
action = for let m from 1 to 12
(p) ->
p null, m
err, data <- async.series action
console.log data
but I having difficulties to have the code works on a nested loop:
action = for let m from 1 to 12
for let d from 1 to 12
(p) ->
p null, (m + "-" + d)
err, data <- async.series action
console.log data
error message:
fn(function (err) {
^
TypeError: object is not a function
As requested by the comment, the compiled js code, generated by Livescript:
var async, action, res$, i$;
async = require('async');
res$ = [];
for (i$ = 1; i$ <= 12; ++i$) {
res$.push((fn$.call(this, i$)));
}
action = res$;
async.series(action, function(err, data){
return console.log(data);
});
function fn$(m){
var i$, results$ = [];
for (i$ = 1; i$ <= 12; ++i$) {
results$.push((fn$.call(this, i$)));
}
return results$;
function fn$(d){
return function(p){
return p(null, m + "-" + d);
};
}
}
The action you got for the nested loop is probably a nested arrays of closures, like [[fn, fn], [fn, fn]]
so you want to flatten them by concatenating:
err, data <- async.series action.reduce (++)

Resources