Function using Mongoose findOne returning undefined? - javascript

I'm trying to use Mongoose to pull a random value from a database using Math.random and Mongoose's findOne. Within my function, the value I'm getting is defined; however when I call the function in another class I am receiving an undefined value. I know this is because of Javascript's asynchronous nature, but I'm not sure how to go about fixing this. Any advice would be appreciated!
export const getRandomItem2 = (req, res) => {
var toReturn;
Item.count().exec(function(err, count){
var random = Math.floor(Math.random() * count);
Item.findOne().skip(random).exec(
function (err, result) {
toReturn = result.description;
console.log('toReturn populated here!' + toReturn);
return toReturn; //this is returning undefined
});
});
}

It's asynchronous code, so in another function when You call it You should pass callback function to get the result:
export const getRandomItem2 = (callback) => {
Item
.count()
.exec((err, count) => {
Item
.findOne(Math.floor(Math.random() * count))
.skip(skip)
.exec((err, item) => callback(item));
});
}
and in some another place:
getRandomItem2(item => {
console.log(item);
});

Related

Function code isn't executed in the right order, async-wait is implemented wrongly

What I'm trying to do in my endpoint, is:
Make an API call, which returns a JSON
for each item: search in our database for it
If it's found, skip it.
If it's not found, push it in an array "Response"
This is my code:
app.get("/test", (req,res) => {
spotifyApi.getUserPlaylists({ limit: 50 })
.then(function(data) {
let finalres = [];
const tbp = data.body.items;
// res.send('ok stop loading');
tbp.forEach(element => locateit(element,finalres));
console.log('This is the length of finalres, which should be 1:', finalres.length);
finalres.forEach(item =>{console.log(item)});
function locateit(item, finalres){
const thisplaylistid = item.id;
collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
if(error) {
return res.status(500).send(error);
}
if(result.length == 0) { // if we don't find this playlist in our DB
console.log('This playlist is not in our database: ');
console.log(thisplaylistid);
finalres.push(thisplaylistid);
}
else{ //if it's already in our DB
console.log('This item is in our database.'); //This should be printed first, six times.
}
});
};
});
});
The content of data.body.items is 7 items, where only the first 6 of them are in our DB. This means, that the last item, should be pushed in finalres.
Therefore, the expected console outcome should be:
This item is in our database.
This item is in our database.
This item is in our database.
This item is in our database.
This item is in our database.
This playlist is not in our database:
3uDLmuYPeRUxXouxuTsWOe
This is the length of finalres, which should be 1: 1
3uDLmuYPeRUxXouxuTsWOe
But instead, I get this:
This is the length of finalres, which should be 1: 0
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This playlist is not in our database:
3uDLmuYPeRUxXouxuTsWOe
It is obviously not executed in the right order. I tried to use async-wait, but I'm struggling to understand where/how it should be implemented. Any help?
This is the part where I tried it, but I get the same console outcome as before:
async function locateit(item, finalres){
const thisplaylistid = item.id;
await collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
...
Update
After reading more about async-wait and promises, I tried to do it this way, but I'm still getting the same output.
app.get("/test", (req,res) => {
spotifyApi.getUserPlaylists({ limit: 50 })
.then(function(data) {
let finalres = [];
const tbp = data.body.items;
// res.send('ok stop loading');
for (const playlist of tbp) {
async function doWork() {
const found = await indb(playlist.id); //returns t/f if found or not found
if (!found){
finalres.push(playlist);
}
}
doWork();
}
console.log('This is the length of finalres and it should be 1: ',finalres.length);
})
});
and the indb function looks like that:
function indb(thisplaylistid){
return new Promise((resolve, reject) =>{
console.log('Searching in our DB...');
collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
if(result.length == 0) { // if we don't find this playlist in our DB
console.log('This playlist is not in our database: ');
console.log(thisplaylistid);
resolve(false); //returns the id
}
else{ //if it's already in our DB
console.log('This item is in our database.'); //This should be printed first, six times.
resolve(true);
}
});
})
}
The problem here is that forEach resolves always resolves as void, no matter if you have async promises running within.
So, your code will return before executing the statements within the forEach
The correct would be wait for all promises to resolve using #Promise.all
Try this instead:
Updated
Using promise as suggested by Bergi instead of callback ( preferable )
app.get("/test", (req, res) => {
spotifyApi.getUserPlaylists({ limit: 50 })
.then((data) => {
// :refac: more meaningful variable names
const playlists = data.body.items
return Promise.all(
playlists.map(
// :refac: destructuring to get only the id, other ain't necessary
async({ id }) =>
collection.find({ id }).toArray()
)
)
.then(playlistsById =>
// :refac: no error occurred fetching playlists
const nonEmptyPlaylists = playlistsById.filter(playlistById => playlistById.length !== 0)
res.status(200).send(nonEmptyPlaylists)
)
.catch(error => {
// :refac: some error occurred at searching some playlist
console.log('error', error)
// :refac: if you might expect that is going to throw an error here, the code shouldn't be 500
return res.status(400).send(error)
})
})
})
As others mentioned, your usage of async/await is wrong. I believe this should work and do what you want, and as a bonus its shorter and easier to read. mastering async and await will simplify your life and save you from callback/promise hell, i highly recommend it
app.get("/test", async (req, res) => {
try {
const data = await spotifyApi.getUserPlaylists({ limit: 50 });
const tbp = data.body.items;
const results = [];
for(let item of tbp) {
const found = await indb(item.id);
if(!found){
results.push(item);
}
}
return res.status(200).send(results);
}
catch(err) {
return res.status(400).send(err);
}
});

node.js waiting for the db query results for the next step

I have a api code like below is written via node.js. Node.js is asynchronous so next step executing before db query result. In that time system had not got the data so gives an error. I have searched callback structure, promise and await but could not solve it. Thanks.
var dateTime = require("node-datetime");
var dt = dateTime.create();
module.exports = {
myOrderImportFromFileAPI: (req, res) => {
//let age = req.params.Age;
// send the player's details to the database
let userName = req.body.userName;
let companyID = req.body.companyId;
let channelId = req.body.channelId;
let orders = req.body.orders;
//default values
let orderTypeId = 1;
let orderStatusId = 1;
let createdDate = dt.format("Y-m-d H:M:S");
let getDate = dt.format("Y-m-d H:M:S");
db.query(`select ID,Code from operationcenters where CompanyID=${companyID}`, (err, result) => {
if (err) {
return res.status(500).send(err);
}
var operationCenterId = result[0].ID
result.find((e) => console.log(e.ID));
});
console.log("operationCenterId");
console.log(operationCenterId);
console.log("channelId");
console.log(channelId);
console.log(orders);
let query = "select ID value, Name label from channels"
db.query(query, (err, result) => {
if (err) {
return res.status(500).send(err);
}
res.send(`1`);
});
},
};
const dateTime = require("node-datetime");
const dt = dateTime.create();
const myOrderImportFromFileAPI = async (req, res) => {
const { userName, companyID, channelId, orders } = req.body
const orderTypeId = 1;
const orderStatusId = 1;
const createdDate = dt.format("Y-m-d H:M:S");
const getDate = dt.format("Y-m-d H:M:S");
try {
const queryData = await queryDatabase()
res.send(queryData).status(200)
} catch(error) {
res.send(error).status(500)
}
}
function queryDatabase() {
const query = "select ID value, Name label from channels"
return new Promise((resolve, reject) => {
db.query(query, (err, result) => {
if (err) {
reject(err)
}
resolve(result)
});
})
}
soomething like above will get you on the way. I haven't tested this so I can't say it works 100% but it is a good starting point. The reason for this is because by default the mysql db doesn't support asynchronous the modern way. That's why many people wrap in Bluebird(?) which is a library for creating promises which is a frequent dependency on drivers and such. So what you are doing is creating an async function with const myOrderImportFromFileAPI = async (req, res) => {}
After that then creating your variables, since you aren't redeclaring them then use const the first line after the function declaration is destructuring the object. Since we know the name of data and naming it as such then you can save lines with that and makes it more readible.
Finally, we wrap the async call in a try catch statement since we are calling a function which returns a promise(we don't need to call name async function queryDatabase since we are not performing anything asynchronous inside)
inside the actual function queryDatabase we will return with a resolved value of result or return a rejected value of err which will be caught in our try catch statment and will send the appropriate response..

getting undefined for a function used in another function [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I'm getting the required result from console.log in getuser(), but I'm getting undefined when used it in another function
let getuser = (id) => {
user.findById(id, (err, user) => {
if (err || !user) {
return "wrong user id";
} else {
let { name, _id } = user;
console.log(JSON.stringify({ name, _id }));
return { name, _id };
}
});
};
I'm getting undefined when i use getuser() in send()
exports.send = (req, res) => {
let {id}=req.body
let a=getuser(req.User._id)
let b=getuser(req.body.id)
console.log(a)
console.log(a)
let message = new messages({
user1: a,
user2: b,
});
message.save((err, saved) => {
if (err) {
return res.json(err);
} else {
return res.json(saved);
}
});
}
im getting undefined in
console.log(a)
console.log(a)
You probably want to use a Promise for the getuser method:
let getuser = (id) => new Promise((resolve, reject) => {
user.findById(id, (err, user) => {
if (err || !user) {
reject("wrong user id");
} else {
let { name, _id } = user;
console.log(JSON.stringify({ name, _id }));
resolve({ name, _id });
}
});
});
The operation looks asynchronous, so you need to wait for the result before continuing. This means that your getuser calls need to handle promise/async values:
let a = await getuser(req.User._id)
let b = await getuser(req.body.id)
Of course, await must be used in a function marked with async. Read up on Promises and async/await if any of this is unclear - they're absolutely imperative for working with databases, APIs etc.. It may be possible to use it immediately by changing your exports line:
exports.send = async (req, res) => {
But I don't know what environment this is for, so I can't say if that will work out of the box.
Please note that I haven't tested this at all, as it's a rather incomplete example to begin with. What I've suggested is merely theoretical.

Create callback in node js module.exports

How to create a callback function inside the module.exports parameter.
I'm trying to do a similar thing as below and I want to know how to implement callback function.
module.js
module.exports = (a, b, callback) => {
let sum = a+b
let error = null
//callback
}
app.js
const add = require(./module.js)
add(1,2, (err, result) => {
}
within your module.exports you need to "invoke" the call back function.
like so
callback(error, sum)
this will return the control back to the app.js add() function.
You implement your callback function here. i.e what you want to do with the result you received.
Here is what your code will look like:-
module.js
module.exports = (a, b, callback) => {
let sum = a+b
let error = null
callback(error, sum) // invoke the callback function
}
app.js
const add = require("./module")
add(1,2, (err, result) => {
if(err) { // Best practice to handle your errors
console.log(err)
} else { // Implement the logic, what you want to do once you recieve the response back
console.log(result)
}
})
You have used sum for your function; but I will be using divide, because that way I can show you the error thing of callback.
your export will look like this
module.exports = {
divide: (a,b,cb) => {
if (b === 0) {
cb('divide by zero', null);
} else {
cb(null, a/b);
}
}
}
and the import like this
var func = require('./testExport').divide;
func(1,2,(err,res) => {
console.log(err,res);
});
func(1,0,(err,res) => {
console.log(err,res);
})
Call backs are simply the function that you send in from the place you are calling the functions. In both function calls (in imported place) you see we are sending in a function as a callback that takes in two arguments.
In the exported place we call that same function with the first parameter as an error and the second as res.
If you want to import your function without require().func, you will have to export the function in default.
module.exports = (a,b,cb) => {
if (b === 0) {
cb('divide by zero', null);
} else {
cb(null, a/b);
}
}
and import it as
var defaultFunc = require('./testExport')
add.js
module.exports = (a, b, callback) => {
if (typeof a !== 'number' || typeof b !== 'number') {
return callback(new Error('Invalid argument passed'), null);
}
let sum = a + b;
callback(null, sum);
};
app.js
const add = require('./add');
add(1, 2, (err, result) => {
if (err) {
console.log(err);
}
console.log(result);
});
Here we are passing error as the first parameter and actual sum as the second parameter to the callback function. Say if we pass a string instead of the number then the first parameter will have the error object and result will be null.
Cheers.

Accessing this object of a callback function from outside

It might be a bit confusing what I'm asking but I'll try to be as clear as I can.
Basically I'm doing unit test with mocha/chai for my Data Access Layer of my Node.JS server. I'm using bluebird to return a promise and an SQLite Databases.
That's my function insert I want to test :
insert(sqlRequest, sqlParams, sqlRequest2) {
return new Promise(function (resolve, reject) {
let insertStatement = this.getDatabase().prepare(sqlRequest);
let getStatement = this.getDatabase().prepare(sqlRequest2);
insertStatement.run(sqlParams, err => {
console.log('this.changes = ', this.changes);
if (this.changes === 1) {
getStatement.all({ $id: this.lastID }, (err, rows) => {
if (err) {
console.log('entered second err');
reject(err);
} else {
resolve(rows[0]);
}
});
} else {
console.log('entered first err ');
reject(err);
}
});
}.bind(this));
}
And that's my test with mocha :
it('insert : Error 2st SQL query', function (done) {
const daoCommon = new DaoCommon();
daoCommon.getDatabase = () => {
return {
prepare: (sqlRequest) => {
return {
all: (sql, callback) => {
let err = {};
let rows = null;
callback(err, rows);
},
run: (sqlParams, callback) => {
let err = undefined;
callback(err);
}
}
}
}
}
daoCommon.insert('', '', '')
.then(success => {
expect.fail();
})
.catch(error => {
expect(error).to.eql({});
})
.finally(function () {
done();
})
});
I want to simulate a test where the this.changes is equal to 1 but I don't know how/where I can set this value. According to what I've read this this object is from the callback function, but I have no idea exactly from where it comes or how to set it for my tests.
Update:
You can set the this of a function you are calling with .call of the method.
In your case calling callback with this.changes value will look like:
var thisObject = {
changes: 1
};
callback.call(thisObject, err);
This will set the value this.changes of your callback function.
The value of this is explained in the API documentation
If execution was successful, the this object will contain two
properties named lastID and changes which contain the value of the
last inserted row ID and the number of rows affected by this query
respectively.
It means that the callback function will always have this.changes. You can not change it unless you set this.changes = something manually, which I don't understand why would you do that.
Thank for #Maxali comment, I will post the answer below :
You can set this when calling the function callback(err) by using .call(). eg: callback.call({changes:1}, err). this will set changes to 1
And note that I had to change this line insertStatement.run(sqlParams, err => { where I have the callback from an arrow function to a function declaration insertStatement.run(sqlParams, function(err) { for this to work. I assume this is due to the this which in an arrow function doesn't refer to the object inside the function itself.

Categories