Stuck with Promise Pending [duplicate] - node.js

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
Using NodeJS and Mongoose I'm trying to create a database file that allows me to query the database and return the result to be able to work with without using Mongoose.
In my database file I have this
db.js
module.exports = {
getUser: function (name) {
var u = User.findOne({username: name}).exec().then(function(user) {
console.log(user);
return user;
});
return u;
}
};
and in my other file calling it I have
var db = require('./db')
var user_info = db.getUser("tom");
console.log(user_info);
When the getUser function is called it correctly prints the information I'm looking for in the db.js file with the following line of code.
console.log(user);
However I can't seem to find a way of getting the database to return the value into user_info, I just get
Promise { <pending> }
I've looked into .then and callbacks but can't seem to figure out how to get it to work I just always end up in an endless cycle of Promise pending.
Thanks

var db = require('./db')
var user_info = db.getUser("tom");
console.log(user_info);
user_info.then(user => {
const username = user.username;
console.log(username);
});
Promises execute. But, you get access to the result on after a .then.

you must call .then on the promise to capture the results:
check here
Promise pending

You need to call .then for getUser.
// db.js
module.exports = {
getUser: function (name) {
return User.findOne({
username: name
}).exec();
}
};
var db = require('./db');
db.getUser("tom").then((user) => {
console.log(user);
}).catch((err) => {
console.log(err);
});

Related

Function that return undefined in node.js. Inside the function I have mongoDB.connect

I tried to make function for my project in the service. This service need to check is user exists in my database, but my function(this function is checking) inside the class return undefined.
This is my code:
const express = require("express");
const mongoDB = require('mongodb').MongoClient;
const url = "here I paste url to my databse(everything is good here)";
class CheckService {
isUserExists(username) {
mongoDB.connect(url, (error, connection) => {
if (error) {
console.log("Error", '\n', error);
throw error;
}
const query = {name: username};
const db = connection.db("users");
const result = db.collection("users").find(query).toArray(
function findUser(error, result) {
if (error) {
throw error;
}
const arr = [];
if (result.value === arr.value) {
console.log(false);
connection.close();
return false;
} else {
console.log(true);
console.log(arr);
console.log(result);
connection.close();
return true;
}
});
console.log(result);
});
}
}
module.exports = new CheckService();
I imported my service to another service:
const checkService = require('./check.service');
After this, I invoked my function from my service like this:
console.log('function:',checkService.isUserExists(username));
I expected good result, but function doesn't return, that I want, unfortunately.
Please help!
There are two problems with your function
it doesn't return anything
toArray() is a promise, so your console.log probably just prints a promise.
Probably you're looking for something like this:
class CheckService {
async isUserExists(username) {
const connection = await mongoDB.connect(url);
const query = {name: username};
const db = connection.db("users");
const result = await db.collection("users").find(query).toArray();
connection.close();
return result.length !== 0;
}
}
You'd then invoke it with something like
await new CheckService().isUserExists(username);
Though, it's worth noting that you probably don't need toArray and instead could use findOne or even count() since all you care about is existence. I probably also wouldn't instantiate a new connection each time (but that's not super relevant)
2 things wrong here. Firstly the first comment is correct. You're only logging the result and not passing back to the caller. Secondly, a quick peek at the mongo docs shows that retrieval methods are promises. You need to make the function async and add awaits where needed.
Mongo:
https://www.mongodb.com/docs/drivers/node/current/fundamentals/crud/read-operations/retrieve/
Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

await doesn't wait for function to end

I know this has been asked a lot but I can't seem to use the existing answers to get my code to work. I am trying to use mongojs to make a single query, then put the results in a global (relative to the scope) variable to avoid nesting multiple callbacks. However, I can't seem to get the code to wait for the query to end before continuing.
async function taskmaster() {
const db = mongojs(mongoUri.tasker);
let currentDoc;
const getTask = async function() {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
console.log(doc);
currentDoc = doc;
});
}
await getTask();
console.log(currentDoc);
// Code to process currentDoc below
}
No matter what I do, console.log(doc) shows a valid MongoDB document, yet console.log(currentDoc) shows "undefined". Thanks in advance!
Inside your async function, you use findOne method() in callback style, so it's totally normal that console.log(currentDoc) shows undefined, because it executes before
currentDoc = doc;
You can promisify the findOne method, to use it with async/await keyword.
I found a tutorial to promisfy a callback style function here, hope it help : https://flaviocopes.com/node-promisify/
--- EDIT ---
I rewrite your code when promising the findOne method, as suggested by O.Jones
async function taskmaster() {
const getTask = async (taskId) => {
return new Promise((resolve, reject) => {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
if(err) {
console.log("problem when retrieve data");
reject(err);
} else {
resolve(doc);
}
});
})
const db = mongojs(mongoUri.tasker);
const currentDoc = await getTask(taskId);
console.log(currentDoc);
}

Retrieving values from database using node and mssql

Here's a simple script i made:
const Nightmare = require('nightmare');
const sql = require('mssql');
const itens = getRecords();
async function getRecords(){
let itensList = [];
const cfg = {
//config here
};
sql.connect(cfg, function(err){
if(err) console.log(err);
let request = new sql.Request();
request.query("<query here>", (err, result) => {
if(err) console.log(err);
itensList = result;
});
return itensList;
});
}
async function getPrices(){
try{
console.log(itens)
}catch(e){
console.log(e);
}
}
getPrices();
Everything works, but when the getPrices() function gets called, here's what's being logged:
Promise { undefined }
What am i missing here?
request.query is being called, but itenslist is being returned before it can be assigned.
Basically, the order of what is happening is:
request.query is called, and starts running the query.
Since request.query is asynchronous, we move on to the next task - returning itenlist
request.query finishes running, and assigns itenlist the expected value after it has already been returned.
To get your desired functionality, I would recommend using callbacks (which node-mssql supports). Alternatively, you could use the await keyword. For instance:
var queryText = 'SELECT 1 AS Value';
var queryResults = await connection.query(queryText);

How to write a static method in Mongoose that gets all documents?

I'm new to Mongoose. I wrote a statics method and a instance method for a Mongoose schema named 'questionSchema' and exported it like so:
var questionSchema = new Schema({
...
})
questionSchema.methods.createQuestion = function(){
return this.save(function(err){
if(err){
return err
};
return 'Saved the question';
});
};
questionSchema.statics.getAllQ = function(){
return this.find({}, function(err, res){
if(err){
return err
};
return res;
});
}
module.exports = mongoose.model('Question', questionSchema)
Then in a route in my Node/Express server, I imported the it as a model, and tried calling the static method, which should return all the documents under the Question model:
const Question = require('../models/question.js');
...
router.post('/qcrud/submit', (req, res) => {
let reqBody = req.body;
var newQuestion = new Question({reqBody});
newQuestion.createQuestion();
})
router.get('/qcrud/getAll',(req, res) => {
let qArr = Question.getAllQ()
res.send(qArr);
});
However, it returns a Query object, not an array like I expected. I looked around and saw on MDN that
'If you don't specify a callback then the API will return a variable
of type Query.'
I did specify a callback, but still got the Query object. First of all, am I using my static and instance methods right? Are the documents even saving? And how do I access the array documents saved?
If you're using Node 8.x you can utilize async/await
This way your code will look more synchronous:
questionSchema.statics.getAllQ = async () => {
return await this.find({});
}
router.get('/qcrud/getAll',async (req, res) => {
let qArr = await Question.getAllQ();
res.send(qArr);
});
You can find a really nice article that is explaining how to use Mongoose with async/await here.

How to handle promise returning with Mongoose using Bluebird?

I have an Account schema, defined with Mongoose, I've setup promises with Bluebird:
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
I've designed a model method for such schema:
accountSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.password);
}
So I got a method which will try to find a user and check for password match:
function login (email,password) {
return Account.findOne({email: email}).then(function (user) {
console.log(user);
user["match"] = user.validPassword(password);
console.log(user);
return user.validPassword(password);
});
}
What's really weird is that the second console.log won't show up any match property for the object.
Here my intention is to return a promise of finding a user and check for password match, however when I invoke login:
login("email","password").then(function(user){...})
User doesn't have a match property, how could I achieve it?
You cannot do both a return prior to invoking the Promise call : return Account.xxxxx AND do a .then() .... its an either or ... I give you both alternatives. Version A we process resultset local to login function :
function login (email,password) {
// notice I no longer have return Account.xxxx
Account.findOne({email: email}) // Account.findOne returns a Promise
.then(function (user) {
if (user) {
user.match = user.validPassword(password);
// execute some callback here or return new Promise for follow-on logic
} else {
// document not found deal with this
}
}).catch(function(err) {
// handle error
});
}
here the caller does :
login("email","password") // needs either a cb or promise
.then(function(userProcessed) { ...
}).
... whereas in Version B we relegate processing to caller to do the .then() logic :
function login (email,password) {
return Account.findOne({email: email});
}
so in caller we have :
login("email","password").then(function(userNotProcessed){...})
Once you have the result set from the findOne, perform some validation on the user , avoid assuming it was found.
Also since Promise is now in ES6, you can use the built in Promise implementation
mongoose.Promise = global.Promise;
Take note that a findOne returns a document, whereas doing a find always gives you an array of 0 or more document(s)
login(email,password){
return new Promise(function(resolve,reject){
Account.findOne({email: email}).then(function (user) {
user.match = user.validPassword(password);
resolve(user)
});
})
}

Resources