How to use chaining promise together with for loops of arrays? - javascript

So, I have this code. I'm trying to debug it in forever. I dont know why it is returning error. I might have some element of promise I forgot or I have something wrong with my array.push 'cause when I look at the log, it throws error on line where I push some objects into array.
Here is my code so far:
router.post('/inventory/product/stocks/add/(:id)', authenticationMiddleware(), function(req, res, next) {
const db = require('../db.js')
var product_no = req.params.id
var cog = req.body.cog
var size_slug = req.body.size_slug
var size_name = req.body.size_name
var rowinserted = 0
var initial_stock = req.body.initial_stock
var stock_id = new Array
var batch_id = new Array
var stock = new Array
var batch = new Array
new Promise(function(resolve, reject) {
console.log('one');
// Getting product product_slug for product_sku
let sql = `SELECT product_slug
FROM inventory_tbl
WHERE product_no = ?`
db.query(sql, [req.params.id], (error, results, fields) => {
if (error) {
throw error;
} else {
var product_slug = results[0].product_slug
resolve(product_slug)
}
})
})
.then(function(value) {
console.log('two');
// Insert product sizes together with its initial stock
for (var x = 0; x < size_slug.length; x++) {
var product_sku = value + size_slug[x]
var slug = size_slug[x]
var name = size_name[x]
var initial_stock = initial_stock[x]
console.log(product_sku);
if (size_slug[x] != '') {
stock.push({
product_sku: product_sku,
product_no: product_no,
size_slug: slug,
size_name: name,
total_stock: initial_stock,
available_stock: initial_stock
})
}
console.log(stock);
}
for (var x = 0; x < size_slug.length; x++) {
var product_sku = value + size_slug[x]
var initial_stock = initial_stock[x]
if (size_slug[x] != '') {
batch.push({
product_no: product_no,
product_sku: product_sku,
production_date: mysql.raw('CURRENT_TIMESTAMP'),
batch_cog: cog,
initial_stock: initial_stock,
stock_left: initial_stock
})
}
console.log(batch);
}
return value
})
.then(function(value) {
console.log('three');
// Insert rows to product_tbl and stock_tbl
for (var i = 0; i < stock.length; i++) {
let sql = `INSERT INTO product_tbl(product_sku, product_no, size_slug, size_name, total_stock, available_stock) VALUES (?, ?, ?, ?, ?, ?)`
db.query(sql, [stock[i].product_sku, req.params.id, stock[i].size_slug, stock[i].size_name, stock[i].total_stock, stock[i].available_stock], (error, results, fields) => {
if (error) throw error
db.query(`SELECT LAST_INSERT_ID() AS id;`, (error, results, fields) => {
stock_id[i] = results[0].id
})
})
sql = `INSERT INTO stocks_tbl(product_no, product_sku, production_date, batch_cog, initial_stock, stock_left) VALUES (?, ?, CURRENT_DATE, ?, ?, ?)`
db.query(sql, [req.params.id, batch[i].product_sku, batch[i].batch_cog, batch[i].initial_stock, batch[i].stock_left], (error, results, fields) => {
if (error) throw error
db.query(`SELECT LAST_INSERT_ID() AS id;`, (error, results, fields) => {
batch_id[i] = results[0].id
})
})
rowsupdated++
}
return value
})
.then(function(value) {
console.log('four');
// Render the web page
if (rowinserted != sizeslug.length) {
req.flash('error', error)
res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
} else {
req.flash('success', 'Data added successfully!')
res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
}
})
.catch(function(error) {
console.log('error');
// Error handler
for (var i = 0; i < rowinserted; i++) {
let sql = `DELETE FROM product_tbl WHERE product_sku = ?`
db.query(sql, [stock_id[i]], (error, results, fields) => {
if (error) throw error
})
sql = `DELETE FROM stocks_tbl WHERE product_sku = ?`
db.query(sql, [batch_id[i]], (error, results, fields) => {
if (error) throw error
})
}
res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
})
})
My log returns:
one
two
error
Edit: The process stops (I'm not sure the specific line but according to the log output) after console.log('two') because I tried putting some log as well after the for loops but they don't proceed there. It just go to the .catch/error.

Instead of outputting a string in console.log('error'); dump out an actual error object that you receive in the catch handler. It will give additional details of why and where it fails. I suspect that the code after console.log('two'); throws an exception and then you unintentionally swallow it below.
Consider splitting your code into separate thematic functions. That way you will be able to maintain and spot the errors (or typos) much easier.

Looking at the output, i can see that console.log(product_sku); this is not getting printed. So, actually the problem is var initial_stock = initial_stock[x]. You have declared the local variable(to your then callback function) with same name as global variable(to your route.post callback functions) and now your global initial_stock variable is masked with local one, which is not an array (actually is undefined). So try changing the variable name to something else in your then block and see if problem disappear.
Hope this helps.

Related

Node.js and SQL: How can I get the value outside of the for loop?

Using Node.js and a MySQL database I'm working on a small project.
I'm trying to loop through an array to get some values out of a MySQL database. I'm searching for the corresponding "medicine ID" to an "medicine name" that the user entered. My Code is working correctly and this is how it looks.
var medizinArray = [];
function appendArray(input) {
medizinArray.push(input);
}
var sqlMedNameToId = "SELECT MedikamentId FROM Medikament WHERE Bezeichnung = ?"
for (var i=0;i<medicineMontag.length;i++){
var montagsMedizin = medicineMontag[i];
mySqlConnection.query(sqlMedNameToId, montagsMedizin, function(err, rows, fields){
if(!err) {
result = rows[0].MedikamentId;
appendArray(result);
} else {
console.log(err);
}
})
}
console.log(medizinArray);
The code is working but I can't get the medizinArray out of the for loop. In my console I get an empty array. When I put the console.log(medizinArray) inside the for loop I get the array that I want.
I'm currently not familiar with Promises. I read about it and saw some other questions but I can't figure out how to implement Promises in my code.
SQL operations are asynchronous, so to obtain the result outside of the callback you need to wrap them in a Promise and call the resolve() function when the operation is successful. Use any of the techniques below:
Async/await technique:
(async function(){
let medizinArray = [];
function appendArray(input) {
medizinArray.push(input);
}
let sqlMedNameToId = "SELECT MedikamentId FROM Medikament WHERE Bezeichnung = ?"
await new Promise(function(resolve, reject){
let e;
for (let i=0;i<medicineMontag.length;i++){
let montagsMedizin = medicineMontag[i];
mySqlConnection.query(sqlMedNameToId, montagsMedizin, function(err, rows, fields){
if(e) return;
if(!err) {
result = rows[0].MedikamentId;
appendArray(result);
} else {
//console.log(err);
e = true;
return reject(err);
}
if(i == medicineMontag.length-1) resolve(result);
})
}
}
);
console.log(medizinArray);//now medizinArray shows up here
})().catch(function(err){console.log(err)});
Promise/then technique:
let medizinArray = [];
function appendArray(input) {
medizinArray.push(input);
}
let sqlMedNameToId = "SELECT MedikamentId FROM Medikament WHERE Bezeichnung = ?"
new Promise(function(resolve, reject){
let e;
for (let i=0;i<medicineMontag.length;i++){
let montagsMedizin = medicineMontag[i];
mySqlConnection.query(sqlMedNameToId, montagsMedizin, function(err, rows, fields){
if(e) return;
if(!err) {
result = rows[0].MedikamentId;
appendArray(result);
} else {
console.log(err);
e = true;
return reject(err);
}
if(i == medicineMontag.length-1) resolve(result);
})
}
}
).then(function(result){
console.log(medizinArray);//now medizinArray shows up here
}).catch(function(err){
console.log(err);
});

Node JS mysql looping query push to array

I'm having some trouble with below code. Can someone please take a look and advise what's going on?
const name = ["a", "b", "c"]
let query1 = ('SELECT name FROM table WHERE name = ? AND status IN ("Pending","Active") limit 1')
if (name instanceof Array) {
async function getNames() {
try {
name.forEach(async (name) => {
let newName = []
let rows = await promisePool.query(query1, name);
if (rows[0].length > 0) {
if (rows[0])
newName.push(rows[0][0].uid_name)
console.log(newName)
}
return (rows);
})
}
catch (err) {
console.log('ERROR => ' + err);
return err;
}
}
await getNames();
console.log(newName) returns (if row is found)
['a']
['b']
how can I push any returning value to the array ? desired output ['a','b'] so I can compare it to the original array.
Thanks in advance,
Ucin
I have changed the code a bit but to be honest I'm still not that of an expert.
const name = ["a","b","c"]
let query1 = ('SELECT name FROM table WHERE name = ? AND status IN ("Pending","Active") limit 1')
if (name instanceof Array) {
async function getNames() {
try {
for(names of name) {
let rows = await promisePool.query(query1,[uid,challenge_type,names]);
return (rows);
}
}
catch (err) {
console.log('ERROR => ' + err);
return err;
}
}
let newVar = await getNames();
console.log(newVar[0])
the newVar only returns 1 line, where there should be 2 names.
name.forEach(async (element) => {
let rows = await promisePool.query(query1,[uid,challenge_type,element]);
if (rows[0].length > 0) {
if (rows[0])
/* newName.push(rows[0][0].uid_name) */
console.log(rows[0])
return (rows);
if I go with forEach...Of, I can console.log it within scope, but return(rows) is undefined
Is there a specific reason you are looping through the names and executing a query per name? If all you want to do is see which of those names in the array already exist in your database you could do it all in a single query. Something like:
const names = ["a","b","c"];
const query = 'SELECT name FROM table WHERE name IN (?) AND status IN ("Pending","Active")';
const result = await promisePool.query(query, [names]);
console.log(result);

Custom prefix using mysql & node.js

var prefix = `SELECT S_PREFIX FROM ServerConfigs WHERE S_ID = ${message.guild.id}`;
connection.query(prefix, function (err, result, fields) {
if (err) return console.log(err);
let res = JSON.parse(JSON.stringify(result[0].S_PREFIX));
const serverPREFIX = res;
It works but sometimes it writes me an error to console.
The error looks like this:
TypeError: Cannot read property 'S_PREFIX' of undefined
Could anybody help me please? Just started learning MYSQL.
result[0] is undefined, which means that your SQL query returned no results, meaning that there were no rows with the S_ID being message.guild.id. Just check that the result array is not empty.
var prefix = `SELECT S_PREFIX FROM ServerConfigs WHERE S_ID = ${message.guild.id}`;
connection.query(prefix, function (err, result, fields) {
if (err) return console.log(err);
if (result.length < 1) {
console.log(`No results for S_ID = ${message.guild.id}`)
return;
}
let res = JSON.parse(JSON.stringify(result[0].S_PREFIX));
const serverPREFIX = res;

Async functions in for loops javascript

I am not experienced with async functions and I would like to perform a request in a for loop. Here's my code:
app.route('/friendlist').post((req, res) => {
var body = req.body;
var list = "";
con.query(`SELECT * FROM player_friends WHERE main_user_id = '${body.player_id}'`, (err, row, fields) => {
if (err) throw err;
async function queryOutUserData(data) {
var rows = await new Promise((resolve, reject) => {
con.query(`SELECT * FROM players WHERE player_id = '${data.player_id}'`, (error, player, field) => {
if (error) {
console.log(error);
return reject(error);
}
resolve(player);
});
});
rows.then(message => {
return message
});
}
for (var i = 0; i <= row.length; i++) {
console.log(row[i].main_user_id);
var result = await queryOutUserData(row[i]);
list = list + ";" + result[0].player_id + ":" + result[0].player_username;
}
console.log(list);
return list;
});
});
Actually here's the full problem: I did some debugging and apparently value i in for loop increases before the promise is resolved. Also as I mentioned I am not familiar with async functions, could you provide me a descriptive resource about how promises and async functions work?
Thanks
NOTE: For better indentation, here's the code: https://hastebin.com/fovayucodi.js
Instead of using async/await I suggest doing everything in one query using WHERE IN rather than one query per player. See if the following fits your needs:
app.route('/friendlist').post((req,res) => {
var body = req.body;
var list = "";
con.query(`SELECT * FROM player_friends WHERE main_user_id = '${body.player_id}'`, (err, row, fields) => {
if (err) throw err;
const playerIds = row.map(player => player.player_id);
con.query(`SELECT * FROM players WHERE player_id IN ${playerIds}`, (error, players, field) => {
for (let player of players) {
list += `;${player.player_id}:${player.player_username}`;
}
});
console.log(list);
return list;
});
});
If you await a promise, it evaluates to the result of that promise, so rows is not a promise, it's the result. So this:
rows.then(message => {return message});
Doesn't make much sense, just do:
return message;
Also, you have an await inside of a regular function, thats a syntax error.
Additionally return list; doesn't do much (if that is express), you might want to return res.json({ list });.
: I did some debugging and apparently value i in for loop increases before the promise is resolved.
I doubt that you can debug code if you can't actually run it because of the syntax errors.
try to use for-of instead just a for.
something like this:
Async Function:
async function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
return
}, 1000)
})
}
Here another function using for and waiting for the finish of loop
async function myFunction() {
const data = [1,2,3,4,5,6,7,8]
for(let i of data) {
const value = await test();
console.log(value)
}
console.log("finish");
}

Terminate mysql connection after multiple queries have executed

I have some node.js code which fetches data from an API in a loop and runs mutliple mysql queries to update some rows.
The issue I have is that the script keeps running until I terminate the mysql connection with connection.end(). I am a newbie in asynchronous code. Where do I call the termination function so that it executes when all the queries have finished executing? What's the right design pattern for this? Would waterfall be any good?
This is a snippet from the code I have at the moment (error handling removed for simplicity):
var connection = mysql.createConnection({ host, user, etc... });
for (var i = 0; i < 10; i++) {
var url = "http://api.com?i="+i;
request(url, function(error, response, body) {
var data = JSON.parse(body);
for (el in data) {
connection.query(
"UPDATE table SET col = ? WHERE symbol = ?",
[
data[el].col,
el
]
);
}
});
}
// this will run before all queries have executed
// resulting in an error
connection.end();
So, the problem here is that you are cycling in a synchronized way through the data here:
var data = JSON.parse(body);
for (el in data) {
connection.query(
"UPDATE table SET col = ? WHERE symbol = ?",
[
data[el].col,
el
]
);
}
while the mysql module handles the query in a callback style:
connection.query(query, function(error, rows, fields) {
if (error) {
return callback(error);
} else {
return callback(null,rows);
}
});
where callback has the signature callback(error,rows), so that you can handle the results in this way supposed to have a reusable function:
var executeQuery = function(query,callback) {
var self=this;
this.connection.query(query, function(error, rows, fields) {
if (error) {
return callback(error);
} else {
return callback(null,rows);
}
});
}
and you can call in your code like
executeQuery(statement, function(error,rows) {
//...
})
That said, you must consider that you are doing multiple queries to your database and it is not recommended to do this in for loop cycle. You should consider to use a better solution that could be a waterfall as you say or a promise all using the Promise paradigma.
Suppose that to have this nice function:
var promiseAllP = function(items, block) {
var promises = [];
items.forEach(function(item,index) {
promises.push( function(item,i) {
return new Promise(function(resolve, reject) {
return block.apply(this,[item,index,resolve,reject]);
});
}(item,index))
});
return Promise.all(promises);
}
that takes as input an array of items and a execution function that is function(item,index,resolve,reject) that has a resolve and reject functions of a Promise, so let's turn your executeQuery function in a Promise as well:
var executeQueryP = function(query) {
var self=this;
return new Promise(function(resolve, reject) {
self.connection.query(query, function(error, rows, fields) {
if (error) {
return reject(error);
} else {
return resolve(null,rows);
}
});
}
Now you can process your data in a totally async way promisyfied:
promiseAllP(data,(item,index,resolve,reject) => {
var query= "UPDATE table SET col = %s WHERE symbol = %s";
// example: prepare the query from item in the data
query = replaceInString(query,item.col,item);
executeQueryP(query)
.then(result => resolve(result))
.catch(error => reject(error))
})
.then(results => { // all execution completed
console.log(results)
})
.catch(error => { // some error occurred while executing
console.error(error)
})
where the replaceInString will help you to prepare the statement
var replaceInString = function() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(1, args.length);
var i=0;
var output = args[0].replace(/%s|%d|%f|%#/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return(output);
},//replace,
This is what we have done here:
Used native Promise only
Turned your mysql query in a promise
Called the statements against your data in a completely asynchronous way
Used a Promise and Promise all paradigma, that let you collect the results of the Promise and return to the caller when all the functions are completed.
Catched errors in all the statements execution
Added a simply way to fulfill statements with parameters
Also notice the arrow function syntax (param1, param2) => that simplify the way to write a function, that can help a lot with the Promise paradigma.
For anyone interested, I ended up solving it by a mixture of promises and counting the queries, something along the lines of this (not sure if this code actually works but the idea is there):
function fetchFromAPI() {
return new Promise((resolve, reject)=>{
var urls = [];
for (var i = 0; i < 10; i++) {
urls.push("http://api.com?i="+i);
}
var data = [];
var requestedUrls=0;
urls.forEach(url=>{
request(url, (err, response, body) {
if(err) reject(err);
data.push(JSON.parse(body));
requestedUrls++;
if(requestedUrls==urls.length) resolve(data);
};
});
}
}
fetchFromAPI().then(data=>{
mysql.createConnection({ user, hostname, etc... });
var processedKeys=0;
data.forEach(el=> {
mysql.query("UPDATE table SET name = ? WHERE id = ?", [el.name, el.id], (err, rows, fields) => {
processedKeys++;
if(processedKeys==data.length) {
connection.end();
}
});
}
}).catch(err=>{
console.error(err);
});

Categories

Resources