I'm trying to use a SQL query with NodeJs but the async part really mess me up. When i print the return it gets "undefined". How can i sync this?
function SQL_test(blos) {
DB.query("Select profesor_id from materiador where materia_id = '"+blos+"';",function (err, results, fields) {
return results;
});
}
console.log(SQL_test(1));
Thanks!
So your answer is currently a promise. You'll have to read about Async and Await to do more synchronous JS.
Most of the JS for NodeJS is currently async. Below is a rewritten version of your example properly utilizing the callback method for your DB.
function callback (err, results, fields) {
if (err) {
console.log(err);
return err;
}
console.log(results, fields);
return results;
};
function SQL_test(blos) {
DB
.query("Select profesor_id from materiador where materia_id = '"+blos+"';", callback);
}
SQL_test(1);
To do the same thing synchronously you have to still have an outer level promise, otherwise Async and Await won't work how you want it to. There's no true synchronous way to handle this is javascript because it executes without waiting for the response.
function sqlTest(blos) {
return new Promise((resolve, reject) => {
DB.query(
`Select profesor_id from materiador where materia_id = '${blos}';`,
(err, results) => {
if (err) return reject(err)
resolve(results)
}
);
}
)
sqlTest(1)
.then(console.log)
.catch(console.error)
Related
Im adding a duplicate to a mysql table and I want to handle elicited ER_DUP_ENTRY error comming back with a Try/Catch block but its just crashing anyway , is there any possible way to handle error and stop application from crashing using a try/catch block?
async function init() {
try {
connection.query(
'SOME INSERT QUERY',
(err, result, feilds) => {
if (err) throw err
console.log(result);
}
);
} catch (e) {
console.log(e);
}
}
init();
The node mysql-library does not support promises out of the box, which means query does not return a promise which you can await. So you can either wrap the query function in a promise yourself:
async function init() {
try {
const duplicateResult = await new Promise((resolve, reject) => {
connection.query(
'SOME INSERT QUERY',
(err, result, fields) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
} catch (e) {
console.log(e);
}
}
or use util.promisify as Always Learning posted alternatively.
The problem is that connection.query returns undefined right away. Your catch is not involved because the call ends before the work is done and will call your callback function later. An exception that occurs during your callback is too late. You try/catch block has already completed.
You can use promisify to wait on it like this though:
const util = require("util");
function init() {
const queryPromise = util.promisify(connection.query);
return queryPromise('SOME INSERT QUERY')
.catch(e => {
console.log("It failed", e);
});
}
init().then(result => {
if (result) console.log("It worked", result);
else console.log("Aww, it didn't work");
});
Can't I use promise for nodeJS mysql query?
// My DB settings
const db = require('../util/database');
db.query(sqlQuery, [param1, param2])
.then(result => {
console.log(result);
})
.catch(err => {
throw err;
});
It is returning: TypeError: db.query(...).then is not a function
You mentioned in the comments that you want logic after the query block to be awaited, without placing that logic inside of the callback. By wrapping the method with a Promise, you can do that as such:
try {
const result = await new Promise((resolve, reject) => {
db.query(sqlQuery, (error, results, fields) => {
if (error) return reject(error);
return resolve(results);
});
});
//do stuff with result
} catch (err) {
//query threw an error
}
Something like this should work
function runQuery(sqlQuery){
return new Promise(function (resolve, reject) {
db.query(sqlQuery, function(error, results, fields) {
if (error) reject(error);
else resolve(results);
});
});
}
// test
runQuery(sqlQuery)
.then(function(results) {
console.log(results)
})
.catch(function(error) {
throw error;
});
mysql package does not support promise. We can use then only a function call returns a promise.You can use mysql2 which has inbuilt support for Promise. It will also make your code more readable. From mysql2 docs:
async function main() {
// get the client
const mysql = require('mysql2/promise');
// create the connection
const connection = await mysql.createConnection({host:'localhost',
user: 'root', database: 'test'});
// query database
const [rows, fields] = await connection.execute(query);
// rows hold the result
}
I would aslo recommend you to learn about callbacks, promise and async-await
I'm having problems understanding the async methods on nodejs.
I have this fragment of code in my controller:
app.get(basePath, function (req, res, next) {
model.generateDB(function (modelErr, modelRes) {
if (modelErr) console.log('Error: ' + modelErr);
next(res.send(modelRes));
});
});
this fragment of code for the model:
generateDB: function (next) {
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
}
and this fragment of code for the db manager
query: function (sql, next) {
var con = mysql.createConnection(config.MySQL);
con.query(sql, function (err, res) {
if (err) next(err, null);
next(null, res);
});
con.end();
}
It works fine for me. the question is how may I have multiple queries with they're responses in the model with only one controller call, like the example (that not works):
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
the idea could be to get an array of errors and responses, but I don't know how to send it when all queries finish. I tried with the .then, but it seems that doesn't works (The error I get using .then is "can't use then on null").
Another solution could be to concat many queries in one, but I tried with "; " separator and doesn't works for me.
So this is using callbacks (.then is for promises). You could create a promise wrapper around it that would let you promisify it and then you could await them or use promise.all if you wanted to run them in parallel.
For example:
function promiseQuery(query, params) {
return new Promise((resolve, reject) => {
BDManager.query(query, params, function (err, res) {
if (err) return reject(err);
return resolve(res);
});
});
}
let arrayOfResponses = await Promise.all([
promiseQuery(query1, params1),
promiseQuery(query2, params2),
promiseQuery(query3, params3),
]);
Just a few things about that - you probably should be inserting values via parameterized inputs. Your SQL library should support that
Also an error will throw a rejection on this. If you want to catch those errors and push them to an array, you could do that as well with a .catch handler.
If you're not using async/await or a version of node that's compatible, you can also do:
Promise.all([]).then(arrayOfResponses => {});
As well and that will give you the array of the responses for any promises you pass in to promise.all.
There are tons of articles on how to use Promises and how they work but that should get you started.
I have 27 mysql update queries on a single table. All these queries are to be run in a transaction mode, like if one operation fails all other updated queries should rollback.
How I will implement this nodejs with promises?
I'm assuming you use mysql driver found here.
According to documentation, this drivers natively supports transactions like this (copied from documentation):
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
connection.query('INSERT INTO log SET data=?', log, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
Since you mentioned promises, you would want to use promise-mysql package which wraps mysql calls in Bluebird promises.
You can use below approach to handle your secnario.
Create an array of queries which needs to be run. In below example I have created queries and their placeholder values and passed them as an array of objects
return a promise to caller which gets resolved when all queries supposed to be run inside a transaction finished
start the transaction
run queries one by one inside transaction block
store the result inside an accumulator as soon as query returns result
if all transactions are complete the commit the transaction and return accumulated result to caller
otherwise rollback the transaction and return the error to caller via promise
Below is the approach which I have mentioned -
function executeTransaction(queries) {
try {
const connection = yield getConnectionObj({/* your db params to get connection */)
let results = []
return new Promise(function(resolve, reject) {
connection.beginTransaction(function (err) {
if (err) throw err
console.log("Starting transaction")
queries
.reduce(function (sequence, queryToRun) {
return sequence.then(function () {
parent.query(queryToRun.query, queryToRun.values)
/* pass your query and connection to a helper function and execute query there */
return queryConnection(
connection,
query,
queryParams,
).then(function (res) {
/* Accumulate resposes of all queries */
results = results.concat(res)
})
}).catch(function (error) {
reject(error)
})
}, Promise.resolve())
.then(function () {
connection.commit(function (err) {
if (err) {
/* rollback in case of any error */
connection.rollback(function () {
throw err
})
}
console.log('Transactions were completed!')
/* release connection */
connection.release()
/* resolve promise with all results */
resolve({ results })
})
})
.catch(function (err) {
console.log('Transaction failed!')
connection.rollback(function () {
console.log('Abort Transaction !!!')
throw err
})
})
})
})
/* End Transaction */
} catch (error) {
return Promise.reject(error)
}
}
I have the following function that gets a hexcode from the database
function getColour(username, roomCount)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
return result[0].hexcode;
});
}
My problem is that I am returning the result in the callback function but the getColour function doesn't return anything. I want the getColour function to return the value of result[0].hexcode.
At the moment when I called getColour it doesn't return anything
I've tried doing something like
function getColour(username, roomCount)
{
var colour = '';
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
colour = result[0].hexcode;
});
return colour;
}
but of course the SELECT query has finished by the time return the value in colour
You have to do the processing on the results from the db query on a callback only. Just like.
function getColour(username, roomCount, callback)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err)
callback(err,null);
else
callback(null,result[0].hexcode);
});
}
//call Fn for db query with callback
getColour("yourname",4, function(err,data){
if (err) {
// error handling code goes here
console.log("ERROR : ",err);
} else {
// code to execute on data retrieval
console.log("result from db is : ",data);
}
});
If you want to use promises to avoid the so-called "callback hell" there are various approaches.
Here's an example using native promises and the standard MySQL package.
const mysql = require("mysql");
//left here for testing purposes, although there is only one colour in DB
const connection = mysql.createConnection({
host: "remotemysql.com",
user: "aKlLAqAfXH",
password: "PZKuFVGRQD",
database: "aKlLAqAfXH"
});
(async () => {
connection.connect();
const result = await getColour("username", 2);
console.log(result);
connection.end();
})();
function getColour(username, roomCount) {
return new Promise((resolve, reject) => {
connection.query(
"SELECT hexcode FROM colours WHERE precedence = ?",
[roomCount],
(err, result) => {
return err ? reject(err) : resolve(result[0].hexcode);
}
);
});
}
In async functions, you are able to use the await expression which will pause the function execution until a Promise is resolved or rejected. This way the getColour function will return a promise with the MySQL query which will pause the main function execution until the result is returned or a query error is thrown.
A similar but maybe more flexible approach might be using a promise wrapper package of the MySQL library or even a promise-based ORM.