token in the below snippet is always undefined. Can someone help me out figuring what's wrong here?
[err, token] = await to(comparePassHash(body.password, user.password));`
comparePassHash = async (pass, hash) => {
bcrypt.compare(pass, hash, (err, token) => {
if (err) TE(err);
console.log('test');
return token;
});
};
to = (promise) => {
return promise
.then(data => {
return [null, data];
}).catch(err =>
[pe(err)]
);
};
It's undefined because that's what comparePassHash resolves.
async keyword won't work as you expect in that case. You're returning token inside .compare function, not inside comparePassHash. You have to wrap bcrypt.compare with a Promise.
const comparePassHash = (pass, hash) => {
return new Promise((resolve, reject) => {
bcrypt.compare(pass, hash, (err, token) => {
if (err)
return reject(err);
console.log('test');
return resolve(token);
});
});
};
Take your function:
comparePassHash = async (pass, hash) => {
bcrypt.compare(pass, hash, (err, token) => {
if (err) TE(err);
console.log('test');
return token;
});
// implicit return: undefined
};
Without an await it will execute bcrypt.compare, it will not wait until it finishes, since it's not a promise, and it will exit the function, returning undefined since you're missing a return statement.
Another way is to use Util.promisify
const { promisify } = require('util');
const comparePassHash= promisify(bcrypt.compare);
// This must be inside an async function
[err, token] = await to(comparePassHash(body.password, user.password));
Related
I'm fairly new to nodejs and have stumbled into a problem with my code.
The documentation for SQL Server and a guide I found on Youtube both handle their code this way, but after starting to use bycrypt I've noticed my function ends after the request is complete although I'm using .then().
Anyways, here's my code so far:
router.post('/login', (req, res) => {
getLoginDetails(req.body.username, req.body.password).then(result => {
console.log(result);
res.json(result);
})
});
async function getLoginDetails(username, password) {
await pool1Connect;
try {
const request = pool1.request();
request.input('username', sql.NVarChar, username);
request.query('SELECT * FROM users WHERE username = #username', (err, result) => {
if (err) {
return ({err: err})
}
if (result.recordset.length > 0) {
bcrypt.compare(password, result.recordset[0].user_password, (err, response) => {
if (response) {
console.log(result.recordset);
return(result.recordset);
} else {
return({message: "Wrong password or username!"})
}
})
return(result)
} else {
return({message: "user not found!"})
}
})
} catch (err) {
return err;
}
}
I tried logging both the request and the return value from the function getLoginDetails and the request log came faster, so I assume it's not waiting for the program to actually finish and I can't figure out why...
Sorry if that's obvious, but I'd love to get some help here!
EDIT:
router.post('/login', async (req, res) => {
// res.send(getLoginDetails(req.body.username, req.body.password))
await pool1Connect
try {
const request = pool1.request();
request.input('username', sql.NVarChar, req.body.username);
request.query('SELECT * FROM users WHERE username = #username', (err, result) => {
console.log(result);
bcrypt.compare(req.body.password, result.recordset[0].user_password, (err, response) => {
if (response) {
res.send(result);
} else {
res.send('wrong password')
}
})
//res.send(result)
})
} catch (err) {
res.send(err);
}
});
This code works, but when I tried to encapsulate it in a function it still didn't work.
#Anatoly mentioned .query not finishing in time which makes sense, but I thought mssql .query is an async function?
Your problem arises from an wrong assumption that callbacks and promises are alike, but on the contrary callbacks don't "respect" promise/async constructs
When the program hits the bottom of getLoginDetails the progrm execution has already split into 2 branches one branch returned you the (empty) result whereas the other one still busy with crypto operations.
Though it is true that an async function always returns a promise but that doesn't cover any future callbacks that might execute inside it. As soon as node reaches the end of function or any return statement the async function's promise get resolved(therefore future callbacks are meaningless), what you can do instead is handroll your own promise which encampasses the callbacks as well
router.post('/login', (req, res) => {
getLoginDetails(req.body.username, req.body.password))
.then((result)=>{
res.send(result);
})
.catch((err)=>{
res.send(err);
})
});
async function getLoginDetails(username, password) {
await pool1Connect
return new Promise( (resolve,reject) => {
try {
const request = pool1.request();
request.input('username', sql.NVarChar, username);
request.query('SELECT * FROM users WHERE username = #username', (err, result) => {
console.log(result);
bcrypt.compare(password, result.recordset[0].user_password, (err, response) => {
if (response) {
resolve(result);
} else {
resolve('wrong password')
}
})
})
} catch (err) {
reject(err);
}
});
}
You didn't return any result to getLoginDetails. Either you use async versions of request.query and bcrypt.compare (if any) or wrap request.query to new Promise((resolve, reject) like this:
const asyncResult = new Promise((resolve, reject) => {
request.query('SELECT ...
...
if (err) {
resolve({err: err}) // replace all return statements with resolve calls
}
...
})
const queryResult = await asyncResult;
I'm trying to read frpm a json file folder withing my program and i want to use a GET list endpoint to read through browser or postman, but i'm getting the above TypeError. Here is my code:
model.js:
const fs = require('fs');
function loadTeams() {
return new Promise((resolve, reject) => {
fs.readFile('./json/prov-nodes.json', (err, data) => {
if (err) reject(err);
const teams = JSON.parse(data);
console.log(teams);
resolve(teams);
});
});
}
app.use(bodyParser.json());
app.get('/list', (req, res) => {
let teams = [];
loadTeams()
.then(function(data){
teams = JSON.stringify(data);
console.log(teams);
**res.send(teams);** //intended to send to browser/postman response
console.log('try...part ..read call');
})
.catch(error => console.log(error))
res.send("My root page");
console.log(teams);
});
The loadTeams function does not return a promise, and therefore you cannot call .then().
You can wrap the function in a promise like this:
function loadTeams() {
return new Promise(function(resolve, reject) {
fs.readFile('./json/prov-nodes.json', (err, data) => {
if (err) reject(err);
try {
const teams = JSON.parse(data);
return resolve(teams);
} catch(e) {
reject(e);
}
});
});
}
In order to use loadTeams as an async function you should turn it into a function that returns Promise with a callback results:
function loadTeams() {
return new Promise((resolve, reject) => {
fs.readFile('./json/prov-nodes.json', (err, data) => {
if (err) reject(err);
const teams = JSON.parse(data);
console.log(teams);
resolve(teams);
});
});
}
I'm trying to generate a screenshot from HTML
async function makeWeeklyReport(meterId, week) {
// get meters from meter
const meter = meters.find(it => it.prm === meterId);
const weeklyData = await generateWeeklyGraph(meter, week);
ejs.renderFile(path.join(__dirname, './views/partials/', "weekly_graph.ejs"), {
}, (err, data) => {
if (err) {
console.log(err)
return
}
console.log(data); // Actually printing good value
return data
});
}
When I invoke this function:
data = await makeWeeklyReport(meterId, week)
console.log("generated WeeklyReport", data) // returns undefined
data is undefined
but when I do console.log(data); in makeWeeklyReport(), it actually prints the html.
I thought the await keyword was supposed to wait the end of async function.
I already tried to put:
res.render(view, {.., async:true, ... }...)
but it is not working
How should I fix it ?
ejs.renderFile is not an async function/does not return a promise and will call the passed callback-function once it has finished rendering. Since you cannot directly return a value from a callback, rhis won't work.
However, you can wrap the call in a promise and resolve it, once the callback is called (note that I'm rejecting the promise in case of an error, you can change this of course and resolve the promise in that case as well instead):
async function makeWeeklyReport(meterId, week) {
const meter = meters.find(it => it.prm === meterId);
const weeklyData = await generateWeeklyGraph(meter, week);
return new Promise((resolve, reject) => {
// get meters from meter
ejs.renderFile(path.join(__dirname, './views/partials/', "weekly_graph.ejs"), {}, (err, data) => {
if (err) {
console.log(err)
return reject(err);
}
console.log(data); // Actually printing good value
resolve(data);
});
});
}
You can then await this function and process the resolved value:
data = await makeWeeklyReport(meterId, week);
This has nothing to do with async / await.
Your return statements are within a callback function, so nothing is returning in your makeWeeklyReport function.
You can use a Promise to unwrap the callback values.
async function makeWeeklyReport(meterId, week) {
// get meters from meter
const meter = meters.find(it => it.prm === meterId);
const weeklyData = await generateWeeklyGraph(meter, week);
const [err, data] = await new Promise(resolve => {
const p = path.join(__dirname, './views/partials/', "weekly_graph.ejs");
ejs.renderFile(p, { }, (err, data) => resolve([err, data]);
});
if (err) {
console.log(err);
return;
}
console.log(data); // Actually printing good value
return data;
}
To improve readability, you could even wrap renderFile as an async function:
function renderFileAsync(path, data) {
return new Promise(resolve => ejs.renderFile(path, data,
(err, data) => resolve([err, data]));
}
Then your makeWeeklyReport function could be written like this:
async function makeWeeklyReport(meterId, week) {
// get meters from meter
const meter = meters.find(it => it.prm === meterId);
const weeklyData = await generateWeeklyGraph(meter, week);
const p = path.join(__dirname, './views/partials/', "weekly_graph.ejs");
const [err, data] = await renderFileAsync(p, { });
if (err) {
throw err;
}
return data;
}
I have the following function:
const findUserByEmail = (email) => {
var user
MongoClient.connect(url, (err, db) => {
const dbo = db.db('users')
dbo.collection('users').findOne({email: email}, (err, result) => {
if (err) throw err
else return result
})
db.close()
}).then((user) => {
return user
})
}
I am trying to return the value of user so that findUserByEmail(email)=user but can't figure out how. I have tried using async/await and promises to return this value to get around Node's asynchronous nature however in each I could not get the value to return to the main function. In this case, return user returns the user to the then() function, which is not correct.
Any help would be much appreciated.
Your very close but there is one thing your missing with your function findUserByEmail it needs to return a Promise. With a Promise you can call resolve in the future with the result from findOne. This will also change how you consume the findUserByEmail function.
Example
const findUserByEmail = (email) => {
return new Promise((resolve, reject) => {
MongoClient.connect(url, (mongoError, db) => {
if (mongoError) {
return reject(mongoError)
}
const dbo = db.db('users')
dbo.collection('users').findOne({ email: email }, (findError, user) => {
if (findError) {
return reject(findError)
}
db.close();
return resolve(user)
})
})
})
}
To consume this function you can use Promise.then(user => ) or the preferred approach of using async/await.
Consuming using .then()
findUserByEmail("email.com").then(user => {
// do something
}).catch(err => {
// do something
})
Consuming using async/await
async function test() {
try {
const user = await findUserByEmail("email.com");
// do something
} catch (error) {
// do something
}
}
I am trying to return Array of tokens stored in Firebase, and I am using 'promise'.
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let tokens = [];
snap.forEach(child => {
if(child.Status != "occupied"){
helper.getToken(child.key,db).then(function(token){
tokens.push(token);
});
}
});
resolve(tokens);
}, (err) => {
reject(err);
});
});
return result;
}
and this is the 'getToken' method from the "helper" module.
exports.getToken=function(uid,db){
return db.ref(`/Tokens/${uid}`).once('value').then(function(result){
return result.val();
});
};
The problem is that every time I push token into the array it all works fine, but when exit getUsersTokens() the array gets empty.
thanks for the help.
The issue is that your result promise is resolving too early because the helper.getToken() is non-blocking, so your forEach will finish running before all of the getToken() calls have finished pushing their token into tokens.
To make things a little easier, you can split your result promise into two promises. The first promise will be in charge of getting snap. The second promise will be in charge of iterating through snap to produce an array of tokens:
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
resolve(snap);
}, (err) => {
reject(err);
});
});
return result.then(snap => {
let prommiseArr = [];
snap.forEach(child => {
if(child.Status != "occupied"){
let p = helper.getToken(child.key,db);
promiseArr.push(p);
}
});
return Promise.all(promiseArr); // resolves to array of tokens
});
}
Promise.all takes in an array of promises, and resolves when all of those promises have also resolved. the promise returned by getUsersToken will ultimately contain an array of tokens, because each promise of promiseArr resolves to a token.
It happens because the promise is resolved with the token array before getToken() resolves itself. You see an empty array because your handler runs before the tokens arrive.
You need to wait on that before resolving. Like this:
function getUsersTokens() {
let dbRef = db.ref('/system/users');
return new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
const tokensPromise = snap
.filter(child => child.Status !== "occupied")
.map(child => helper.getToken(child.key, db));
resolve(Promise.all(tokensPromise));
});
});
}
Promise.all as pointed out by #André Werlang and #Christian Santos it perfect here is an example using reduce way
function getUsersTokens() {
let dbRef = db.ref('/system/users');
let result = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
snap.reduce((chain, child) => {
return chain.then(array => {
return helper.getToken(child.key,db).then(function(token){
return array.push(token);
});
});
}, Promise.resolve([])).then(tokens=>{
resolve(tokens);
});
}, (err) => {
reject(err);
});
});
return result;
}