I am trying to get some data from the mongoDB and store that in an array, then pass that array into the ejs file. The problem seems to be that while mongo is querying the results, the code after the db Code executes and an empty array is sent to ejs. Results come after execution of the render function and therefore no data is sent to ejs..
app.get('/', (req, res) => {
var batData = [];
//console.log("get req");
MongoClient.connect(url, (err,db)=>{
if(err) throw err;
console.log("Enter DB");
var dbo = db.db("MatchDB");
batData = dbo.collection("Batting").find().toArray((err,res)=>{
console.log("Query Success");
});
console.log("Exit DB");
db.close();
})
// batData remains empty when these lines of code executes.
res.render('index', {
batting: batData
});
});
Output is in this order :
Enter DB
Exit DB
Query Success
Expected Order:
Enter DB
Query Success
Exit DB
Use promise here
//change your query to function
functon query(){
//now here return a promise
return new Promise((resolve, reject) => {
MongoClient.connect(url, (err,db)=>{
if(err) reject(err) // reject the err
var dbo = db.db("MatchDB");
dbo.collection("Batting").find( (err, data) => {
console.log("Query Success");
batData = data//save your data here or do anything
db.close(); //close the db
resolve(batData) //this will get returned to the caller
});//dbo find ends
}) //mongo client ends
})//promise end
}//function ends
//now in your app.get route
// see here i marked this async, for using await
app.get('/', async (req, res) => {
let batData = await query() // this will wait until it gets resove or rejected
res.render('index', {
batting: batData // now you will have data here
});
});
Some points here for your help
A promise gets resolved or rejected
the caller function for the promise will wait until it gets the result back from the promise function, again you can handle this if you don't want to wait for the promise to get finished
async await are just way to handle promise in a more neat way removing callback hells from the code
whenever you need to use await then you have to mark its function in which it lies async as you can see I did in the app.get callback function
now await will block the code until it gets completed, rejected or resolved
after then it will go to the res.render part of the code
batData is declared as an Array but then later in your code you set it equal to the find query. You should .push() it instead or save it to a new variable and then .push() it afterwards.
Also, using const (instead of var) on batData would have thrown an error instead thereby showing this error. Unless you are using var for supporting older code, use const and let instead.
Related
I want to make 2 MySQL queries from one GET request, knit the results together and return them. But I'm having difficulty passing the result of the first query to the next then() block.
const express = require('express');
const router = express.Router();
const axios = require('axios');
const con = require('../../db');
router.get('/:id', (req, res, next) => {
axios.get('/').then(docs => { //get one book by its id number
const sql = "SELECT title, line, mage_edition FROM books WHERE id=" + req.params.id;
con.query(sql, (err, result) => {
if (err) {
throw err;
}
return result; //"result" shows proper value here
});
}).then(docs => { //get all the listings that appear inside that book
//How do I get "result" here?
const sql2 = "SELECT l.entry_id, e.title, e.kind, e.sort FROM links l INNER JOIN entries e ON l.entry_id = e.id WHERE book_id=" + req.params.id;
con.query(sql2, (err, result2) => {
if (err) {
throw err;
}
//combine result and result2, then give back with res.status(200).json()
})
});
});
module.exports = router;
Any help would be greatly appreciated. I finished a training video series on asynchronous JavaScript but I can't figure this one out.
The issue is that your query call doesn't return a promise, it just calls the callback when it's done. If whatever database library you're using doesn't support returning promises, I'd suggest creating wrapper functions for your query methods that return a promise instead. It'll make chaining them into promises much easier.
Something like this:
function queryPromise(con, sql) {
return new Promise((resolve, reject) => {
con.query(sql, (err, result) => {
if (err) { return reject(err); }
return resolve(result);
});
});
}
use the return key word to return the result within the then() block. This should m are the data available for the next block.
Bear in mind that if you need an in-order execution of the then blocks, you would need to return a Promise instead of resolved values
To return from one .then to next .then you should return the value from previous then. Your code is not working because you are returning your result from a callback function. You can't return value from callback function.
Check if your .query function returns any promise or not. If yes then you should do something like this const result = awati con.query(sql);.
Or, you can promisify your .query function and then await it.
The save function works perfectly but, the find function just below it returns null on searching the save object which is previously saved.
Here is the code:
var mongoose = require("mongoose");
var User = mongoose.model("User");
module.exports.register = function(req, res) {
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.setPassword(req.body.password);
user.save(function(err) {
var token;
token = user.generateJwt();
res.status(200);
res.json({
token: token
});
});
User.findById(user._id, function(err, particularDocument) {
if (err) {
console.log(err);
} else {
console.log(particularDocument);
}
});
};
User.findById() return null. Even User.find({}) returns null.
Both saving and finding data from the db is asynchronous in your code.
Since the results of the find depend on the save being finished, you must wait for the save to finish before calling find (or findById).
At the moment, you are executing them at the same time:
// call save but don't wait for it to finish
user.save(/* callback when save is done. this is executed sometime after all other code is done executing. */);
// call findById but don't wait for it to finish
User.findById(user._id, /* callback when find is done. this is executed sometime after all other code is done executing. */);
// continue here before any of the callbacks are executed
But you should execute them serially:
user.save(function() {
User.findById(user._id, function(err, user) {
// get user here
})
// ...
});
The reason this is necessary is because asynchronous code runs some time after all the synchronous code is finished.
In other words, no asynchronous code runs until the current segment of synchronous code is finished.
I have a Node.js function to fetch some value from DB table
var GetPoints = function(ibmdb, dbConnection, msisdn) {
ibmdb.open(dbConnection, function(err, conn) {
if (err) {
//Error
} else {
conn.query('SELECT id,msisdn,points FROM t_points WHERE msisdn =' + msisdn, function(err, data) {
console.log(err);
if (!err) {
conn.close(function(err) {
if (!err) {}
});
consele.log(data);
//return data[0].POINTS;
} else {
//Error
}
});
}
console.log("points" + points);
});
}
I want to know how I can access the data object when I call this function from outside
var data = GetPoints(ibmdb, dbConnection, msisdn);
The value is coming correctly when I do a console.log
You can't return the value from an async function directly. Promises are generally used this situation. You return a promise which can later be called .then upon to retrieve the said value.
var Promise = require('bluebird');
var GetPoints = function(ibmdb, dbConnection, msisdn) {
// return a Promise
return new Promise(function(resolve){
ibmdb.open(dbConnection, function(err, conn) {
if(err) throw err; // throw the error for it to be caught
…
conn.query('SELECT …', function(err, data) {
if(err) throw err;
…
consele.log(data);
//return data[0].POINTS;
resolve(data);
}); }); }); }
GetPoints().then(function(data){
// do something with data
}).catch(function(err){
// handle err
});
Additionally, Bluebird has a promisify function that turns an async function (that takes a callback) into a function that returns a Promise. It makes the above code much simpler:
Note: Although I was reluctant because if you're using MySQL with which the promisification could be a little tricky: 1, 2. But For now I've added .promisifyAll where it might seem redundant as it will likely be executed more than once, but hopefully bluebird's promisification is smart enough to handle this. Nonetheless if you manage to promisify more efficiently you can just remove the redundant promisifyAll and just use X.yyyAsync methods as described:
function GetConnection(ibmdb, dbConnection, msisdn){
Promise.promisifyAll(ibmdb);
return ibmdb.openAsync();
}
function getData(conn){
Promise.promisifyAll(conn);
return conn.queryAsync('SELECT …');
}
GetConnection()
.then(getData)
.then(function(data){
// do something with data
})
The callback function you gave after the SQL query gets executed asynchronously. Instead of trying to get that data outside the function, you should try to perform whatever you need to do inside. Keep in mind you can create another function and call it with the data to continue your work.
I am running a MySQL query inside a .js file running on Node JS. I have the connection setup ok and the query works but when I try returning the result back to the original call it doesn't seem to work.
function sqlQuery(query){
console.log("Running query");
var conn = connection();
var result = false;
conn.query(query, function(err, rows, fields) {
conn.end();
if (!err){ result = rows; } else { result = err; }
});
return result;
}
var loginResult = sqlQuery("SELECT * FROM `players`");
console.log(loginResult);
If I use the following code it does write the result to the console inside the query but not the final "loginResult". I am not getting any errors so my question is - is there an error in the way I am getting the returned result?
if (!err){ result = rows; console.log(rows); } else { result = err; }
Virtually everything in Node.js is asynchronous, and SQL query functions definitely are. You're calling conn.query(query, callback), which means that query is called, and then once there is a result at some point in the future, your callback function gets called with the result for you to work with. So:
conn.query(query, function runThisEventually(err, rows, fields) {
if (err) {
console.error("One or more errors occurred!");
console.error(err);
return;
}
processResults(rows, fields);
});
You won't get the result immediately after calling conn.query(...), so your code gets to do "other things" in the mean time, and at some point, your callback will be triggered and you can pick up result processing there.
I'm building a twitter clone using Node.js and MongoDB with mongoose. My Tweet model has body, user and created fields where user is the id of the user who has created the tweet. Now I'm building the API. I want when I make a GET request to receive a list of all the tweets (/api/tweets/) but except the user field (which returns only the id of the user) I want to get the whole user object so that I can display information about the tweet owner in my front-end part. I ended up with the following code.
exports.all = function (req, res, next) {
Tweet.find({}, function (err, tweets) {
if (err) return res.json(400, err);
var response = [];
tweets.forEach(function (element, index, array) {
var tweet = {};
tweet._id = element._id;
tweet.created = element.created;
tweet.body = element.body;
User.findById(element.user, function (err, user) { // <- This line
if (err) return res.json(400, err);
tweet.user = user;
});
response.push(tweet);
});
return res.json(response);
});
};
It works perfectly except that it doesn't add the user info. The problem is in the line I have marked. When javascript comes to that line, it tries to make it "parallel" and continues with the code without executing the callback function. But then it pushes the tweet object that doesn't have yet user info. How can I fix this?
You're going to want to use the async library. It will make your life much easier.
// inside `Tweet.find`
async.each(tweets, function(done) {
// do stuff to tweets
User.findById(element.user, function(err, user){
if (err) return done(err);
// do stuff with user
done();
});
}, function(err) {
// called when done
res.json(response);
});
The issue is that res.json sends the response so it doesn't matter that findById is being called. You need to call res.json once everything is done.
You can do this in several ways, but the easiest one with your existing code is to just keep a counter:
var tweetCount = 0;
tweets.forEach(/* snip */) {
User.findById(element.user, function (err, user) {
tweet.user = user;
tweetCount++;
response.push(tweet);
if (tweetCount == tweets.length) {
res.json(response);
}
});
});
You can use Q promise library to sync. It is simple and easy to use.
Response will only be send when whole promise chain is compeleted
var result = Q();
tweets.forEach(function (tweet, i) {
result = result.then(function () {
// do your stuff
}
result.then(function () {
res.json(response); //successfully completed
});