Node.js not executing last expression in callback - javascript

I’ve written this code to query a MySQL database in Node 8:
const getDB = require('./db');
function getCrawls(cb) {
const db = getDB();
const rows = db.query('select * from crawls', (err, result) => {
console.log('got result', result);
cb(result);
});
}
getCrawls(c => console.log('done', c));
However, I only get the following output when I run it:
got result []
Now the strange thing is it works if I add another console.log after the callback is called:
const getDB = require('./db');
function getCrawls(cb) {
const db = getDB();
const rows = db.query('select * from crawls', (err, result) => {
console.log('got result', result);
cb(result);
console.log('complete');
});
}
getCrawls(c => console.log('done', c));
Now I get this output:
got result []
done []
It seems that the last statement of the query callback isn’t being called, does anybody have any idea why this could be happening?
Edit:
Here's the code for getDB
const mysql = require('mysql');
function getDB() {
const db = mysql.createConnection({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
})
db.connect();
return db;
}
module.exports = getDB;
It sounds like the error may be related to the MySQL module?

Using the info we have from your code, it should work fine; looking at the following snippet.
It seems that your code is stuck executing cb(result); maybe there is an un-catched exception there?
function query(data, callback) {
setTimeout(() => {
callback(false, ['result']);
}, 1000);
}
function getCrawls(cb) {
const rows = query('select * from crawls', (err, result) => {
console.log('got result', result);
cb(result);
console.log('complete');
});
}
getCrawls(c => console.log('done', c));

Related

Function is returning before SqlQuery finishes

I have a function that has a query that connects to a database and does a basic SELECT query, The query works fine, however the function appears to be returning before the query finishes all of its steps. In this case I have the Hello World Console log and the array is undefined. But if I setTimeout for one second and then log out the "NewArray" it has all the data from the query. So my question is: How do I return this data after the query has finished doing its magic.
async function getData() {
let newArray = []
let db = new sqlite3.Database('PATHTODB',sqlite3.OPEN_READWRITE, (err) => {
if (err) {
return console.error(err.message);
}
});
let selectQuery = `SELECT * FROM TableName;`
await db.all(
`${selectQuery}`,
(err, row) => {
console.log(row)
row.forEach(x => {
newArray.push(x)
})
}
)
console.log("Hello World", newArray)
return newArray
}
Tried using ASYNC/AWAIT
db.all(query, callback) is not an async function to await. It's just invoking it's callback asynchronously. Try promisfying it as follows;
async function getData() {
let newArray = []
let db = new sqlite3.Database('PATHTODB',sqlite3.OPEN_READWRITE, (err) => {
if (err) {
return console.error(err.message);
}
});
let selectQuery = `SELECT * FROM TableName;`,
row = await new Promise((resolve,reject) =>
db.all(`${selectQuery}`, (err,row) => err ? reject(err)
: resolve(row)));
console.log(row);
row.forEach(x => newArray.push(x));
console.log("Hello World", newArray);
return newArray
}

ASYNC AWAIT function .find() mongodb

How can i save a variable from async function .find from mongodb? Inside the function console.log its working (prints the right value) and outside the function it is undefined.
var list;
MongoClient.connect(uri, function(err, db) {
var dbc = db.db("chat");
dbc.collection("chat_messages").find({user1: data.from, user2: data.to}).toArray(function (err, result){
console.log(result[0].msgs); <---- here its working
list = result[0].msgs;
});
db.close();
});
console.log(list); <---- here its not working
try work with Promises, look that sample:
const list = new Promise(function (resolve, reject) {
MongoClient.connect(uri, function(err, db) {
var dbc = db.db("chat");
dbc.collection("chat_messages").find({user1: data.from, user2: data.to}).toArray(function (err, result){
if(err) {
reject(err);
} else {
resolve(result);
}
db.close();
});
});
});
Then use as promise if you need:
list.then(arrayList => {
console.log(arrayList[0].msgs);
}).catch(err => console.log(err.message));
OR, force to run sync
const msgs = (await list)[0].msgs;
Here's how you would do it with async await.
async function getList() {
let db = await MongoClient.connect(url);
let dbo = db.db("testdb");
return await dbo.collection("cars").find({}, { projection: { _id: 0, name: 1} }).toArray()
}
listCars().then(cars => {
console.log(cars); //You will get your results here
})
The mongoDB queries in nodejs like the one above are asynchronous so you cannot get the return value in a variable directly. You either have to use a callback function or .then() to use promises like in the example above.
The toArray() function you are using is a MongoDB method, the one you mentioned in your question is a jQuery one. This is the correct documentation for your reference -> https://docs.mongodb.com/manual/reference/method/cursor.toArray/
You need to make the entire code block into an async-await code block, or take in the help of then block
let conn = await client.connect(url);
let db = conn.db("test");
const results = await db.collection("cars").find({}, { projection: { _id: 0, name: 1} }).toArray()
here results will have your output

Waiting for all mySql requests to finish before rendering page with express

I have a node.js server which queries a database after a user selects what types of lockers he wants to see the status of. A locker has various statuses : colonised, non_renewed, free, occupied. Obtaining any of these categories is done via a specific request.
What I would like is, if the user chooses all options, to be able to do all requests and put all rows into an array, then render a webpage with it. The problem is asynchrounous, ```connection.query```` does not return sequentially, and I do not know how to solve this.
app.get('/admin/sites/lockers/*', function (req, res) {
console.log("GET /admin/sites/lockers");
let parsedQs = querystring.parse(url.parse(req.originalUrl).query);
console.log(parsedQs);
let resultArray = [];
if(parsedQs['colonised']) {
let query = fs.readFileSync('./sql/colonised_lockers.sql', 'utf8');
connection.query(query, function (err, results, fields) {
resultArray.push(results[0]);
console.log("colonized :", resultArray);
});
}
if(parsedQs['non_renewed']) {
let query = fs.readFileSync('./sql/non_renewed_lockers.sql', 'utf8');
connection.query(query, function (err, results, fields) {
resultArray.push(results[0]);
console.log("non_renewed :", resultArray);
});
}
if(parsedQs['free']) {
let query = fs.readFileSync('./sql/free_lockers.sql', 'utf8');
connection.query(query, function (err, results, fields) {
resultArray.push(results[0]);
console.log("free :", resultArray);
});
}
console.log("finally :", resultArray);
res.render('admin/lockerView.ejs', {lockers : resultArray });
});
You can try to find in the documentation of the tool you are using to connect to DB approach based on Promise rather then callback. But you can also wrap your connection.query into Promise and use async await in order to have "like sync" flow in the handler. Something like:
function makeDBQuery(query) {
return Promise((resolve, reject) => {
connection.query(query, function (err, result) {
err ? reject(err) : resolve(result);
});
});
}
app.get('/admin/sites/lockers/*', async function (req, res) {
console.log('GET /admin/sites/lockers');
const parsedQs = querystring.parse(url.parse(req.originalUrl).query);
console.log(parsedQs);
const resultArray = [];
if (parsedQs['colonised']) {
const query = fs.readFileSync('./sql/colonised_lockers.sql', 'utf8');
const result = await makeDBQuery(query);
resultArray.push(result);
}
if (parsedQs['non_renewed']) {
const query = fs.readFileSync('./sql/non_renewed_lockers.sql', 'utf8');
const result = await makeDBQuery(query);
resultArray.push(result);
}
if (parsedQs['free']) {
const query = fs.readFileSync('./sql/free_lockers.sql', 'utf8');
const result = await makeDBQuery(query);
resultArray.push(result);
}
console.log('finally :', resultArray);
res.render('admin/lockerView.ejs', { lockers: resultArray });
});
ofcourse you need some errors handler here and you can move repeated code into additional function but this is another story)

bcrypt compare: callback not triggered

I'm having a strange issue with bcrypt.compare().
Check this:
function logUserIn(redirectRouteName) {
setIsLogin(true);
const fields = User.getPropertiesName();
Base('Customers').select({
maxRecords: 1,
fields,
filterByFormula: `email = "${state.username}"`
}).eachPage((records, fetchNextPage) => {
const user = new User(records[0].fields);
bcrypt.setRandomFallback((len) => {
const buf = new Uint8Array(len);
return buf.map(() => Math.floor(isaac.random() * 256));
});
bcrypt.compare(state.password, user.password, (err, res) => {
// console.log('hello'): returns nothing...
if(res){
setUserData(user);
setIsLogin(false);
props.navigation.navigate(redirectRouteName);
}
});
}, (err) => {
if (err) { console.error(err); return; }
});
}
My issue is, bcrypt compare callback is not triggered. I put logs around it, it's fine, state.password and user.password do contains the expected values. In fact nothing in bcrypt.compare is exectued, so... I'm lost here. Any ideas?

How to repeatedly execute nodejs mysql query until desire result come out?

conn.connect(function (err) {
if (err) throw err;
var prizeCode = generatePrizeCode();// this method return a 5-digit code
var query = "SELECT * FROM user WHERE code = ? ";
var values = [[prizeCode]];
conn.query(query, [values], function (err, result) {
if (err) throw err;
// Here I want to re-execute the above query if the result is not empty
});
});
In the above code, I want to execute query as long as the result contain data. But I cannot use the loop like following pseucode,
// if the result is empty, the generated code does not exist in database. So it can be used.
while(result.length != 0){
var result = conn.query("SELECT * FROM user WHERE code = abc");
}
How can I achieve this?
You'll have to use callbacks or promises/async functions. Here's how it could be written with callbacks:
const mysql = require('mysql');
const retryLimit = 50;
const connection = mysql.createConnection({
database: 'test',
host: 'localhost',
password: 'hunter2',
user: 'dave',
});
function getData(attempts, cb) {
if (attempts < retryLimit) { // we haven't exausted our attempts yet
const code = generatePrizeCode();
connection.query('SELECT * FROM user WHERE code = ?', code, (err, result) => {
if (err) {
return cb(err);
}
if (result.length > 0) { // code already exists
getData(attempts + 1, cb); // recurse
} else { // this is a new code
cb(null, code); // return the new code via the callback function
}
});
} else { // we have exausted our attempts
return cb(new Error('retry limit exceeded'));
}
}
getData(0, (err, code) => {
// do what you want here, e.g., console.log(code)
connection.end();
});
Note that this can cause node to crash if you recurse too many times and exceed the maximum call stack. One way around that is to call setImmediate or setTimeout instead of recursing directly. Instead of the line
getData(attempts + 1, cb); // recurse
use
setImmediate(() => {
getData(attempts + 1, cb); // recurse
});
or
// recurse, using setImmediate every 1000th time
if (attempts % 1000 === 0) {
setImmediate(() => {
getData(attempts + 1, cb); // recurse
});
} else {
getData(attempts + 1, cb); // recurse
}
Using promises and async/await style would clean this up a lot and probably look more like what you're used to (note this uses the promise-mysql library):
const mysql = require('promise-mysql');
async function getData(connection) {
const retryLimit = 50;
for (let i = 0; i < retryLimit; i++) {
const code = generatePrizeCode();
const result = await connection.query('SELECT * FROM user WHERE code = ?', code);
if (result.length === 0) { // this code doesn't exist yet
return code;
}
}
throw new Error('retry limit exceeded');
}
(async () => {
try {
const connection = await mysql.createConnection({
database: 'test',
host: 'localhost',
password: 'hunter2',
user: 'dave',
});
try {
const code = await getData(connection);
// do what you want here, e.g., console.log(code)
} finally {
connection.end();
}
} catch (err) {
console.log(err);
}
})();
basically you gonna need to work with async\await or promises(pretty much the same).
https://javascript.info/async-await
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
this way your gonna be able to only return when you get the right result for you.
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'YOUR_HOST_NAME',
user : 'YOUR_USER_NAME',
password : 'YOUR_PASSWORD',
database : 'YOUR_DATABASE'
});
pool.getConnection(function(err, connection) {
// connected! (unless `err` is set)
});
connection = mysql.createPool
var retryLimit=0;
var getData = function () {
if(retryLimit <= 50)
{
retryLimit++;
connection.query('SELECT * FROM user WHERE code = abc', function (error, results, fields) {
if (error) {throw error;}
else if(result.length <1) {
getData();
//call the same method again.
}
});
}
}
getData();
You could add else statements and do whatever you want.

Categories

Resources