Hiyas. I have a simple app whereby a client is expecting a promise as a result, but upon calling the resolve() method, the promise keeps returning undefined as the result.
The client code:
UsersRepo.findOneAsync({id: id}).then(function(err, result) {
console.log("UserService promise resolution", err, result);
});
This outputs "null" and "undefined", for err and result, respectively
The code that is doing the work and returning the promise:
findOneAsync: function(args) {
var where = ""; //omitted
var promise = new Promise(function(resolve, reject) {
db.query("Select * from users" + where + " limit 1", function(err, result) {
var res = { id: 1, username: 'username', firstName: 'First', lastName: 'Last' };
if(err != null) {
console.log("REJECT", err);
reject(err);
}
else {
console.log("RESOLVE", res);
resolve(null, res);
}
});
});
return promise;
}
As you can see, I'm just returning some static data for this test (the 'res' variable). In the client code, the console statement always prints out:
UserService promise resolution null undefined
I don't get this. It looks like I'm doing everything correctly: calling the resolve() method with the data, and the client code is using .then(function(err, result)) properly, so it seems. Why isn't data being received from the client end?
Thanks for any help!
==>
Solution:
As mentioned below, Bluebird's reject and resolve only take one argument. The first 'null' was only being seen. Changing the code to 'resolve(res)' worked. Thanks guys.
Resolve/Reject both accept a single parameter ... hence why your result is undefined, as the second value you pass to resolve is never used, and the first is null
What you want to do instead is
UsersRepo.findOneAsync({id: id}).then(function(result) {
console.log("UserService promise resolution", result);
}).catch(function(err) {
console.log("UserService promise error", err);
});
findOneAsync: function(args) {
var where = ""; //omitted
var promise = new Promise(function(resolve, reject) {
db.query("Select * from users" + where + " limit 1", function(err, result) {
var res = { id: 1, username: 'username', firstName: 'First', lastName: 'Last' };
if(err != null) {
console.log("REJECT", err);
reject(err);
}
else {
console.log("RESOLVE", res);
resolve(res);
}
});
});
return promise;
}
Bluebird's then does not take in an err. Instead, then takes in two functions, the first being for resolve, and the second for being reject. you can also use catch for handling errors.
http://bluebirdjs.com/docs/api/then.html
Edit: #JaromandaX is correct, the resolve function should only take in one parameter.
You should only resolve(res), in addition to not expecting the error object to get passed into the then callback.
Related
I hope you can help me. I am developing a functionality that reads a series of data (data is taked from csv file) and checks if it has to put it in a list or not. The problem comes when I start to check the data (through promises) since it gives me an error telling me that the rejected promise has not been captured. You will need to use the following:
// -npm install email-existence
const emailExistence = require("email-existence");
The code:
function checkEmailExistencePromise(element) {
return new Promise((resolve, reject) => {
emailExistence.check(element.email, (error, response) => {
if (error) {
reject(error);
return;
}
// If the person has false email the promise will be save (as object) with "Exists" attribute in false.
if (!response) {
resolve({
name: element.name,
phone: element.phone,
email: element.email,
document: element.document,
weight: element.weight,
tags: element.tags,
exists: false,
});
return;
}
// If the person has valid email the promise will be save (as object) with "Exists" attribute in true.
resolve({
name: element.name,
phone: element.phone,
email: element.email,
document: element.document,
weight: element.weight,
tags: element.tags,
exists: true,
});
});
}).catch(() => {
throw console.error();
});
}
// I call the function that will write the CSV file with valid email records.
checkEmails();
// This function collects the promises of the "checkEmailExistencePromise" function and dumps them into an array.
async function checkEmails() {
const promises = sinRepetidos.map((element) =>
checkEmailExistencePromise(element)
);
const values = await Promise.all(promises);
// Here we go through the promises (which are also objects) and those with the true attribute I put them in a new array that will be printed later.
values.forEach((element) => {
if (element.exists === true) {
checked.push(element);
}
});
Because checkEmailExistencePromise() can throw an error (both through the reject() and the throw call), you need to wrap your
const values = await Promise.all(promises);
call in checkEmails() in a try..catch as well, like so
let values = null;
try {
values = await Promise.all(promises)
} catch (e) {
console.error(e)
}
// do something with values, if it's not null
Edit
As you most likely don't want checkEmailExistencePromise to throw an error, you can replace it with this:
function checkEmailExistencePromise(element) {
// NOTE: we're making is so that this promise never rejects - if there's
// an error in there, we'll assume that the email isn't valid
return new Promise(resolve => {
emailExistence.check(element.email, (error, response) => {
let exists = false;
if (error) {
// we can log the error, to make sure we're not doing anything wrong
// that needs to be fixed - some errors can be legit, though
console.error(error);
}
// NOTE: we should probably actually check the response
if(response) {
exists = true;
}
resolve({
name: element.name,
phone: element.phone,
email: element.email,
document: element.document,
weight: element.weight,
tags: element.tags,
exists
})
});
})
}
We take any error to mean that the email isn't valid.
Also, if element only contains those 6 properties (name, phone, email...), then you can simplify the resolve further with something like this:
resolve(Object.assign({},element,{exists}))
This will make a shallow clone of the object and add the exists property to it
The following code is returning undefined when I attempt to execute the error, callback capability. I don't understand why the json isn't returning as the console log outputs the complete query.
Here is the invoked function that is producing the json into the log.
exports.getThem = (req) => {
var addy = req.body.email;
Picks.findOne({ 'email' : addy }, (err, theResults) => {
if (err) {
return ({ 'msg': err });
}
console.log("made the query " + JSON.stringify(theResults));
return theResults;
});
};
Above, theResults print "made the query " and a complete JSON string.
The invoking code will execute and return to Postman the following JSON without the theResults JSON.
The console.log "log this" never executes and the 2nd log prints "after the log" undefined.
{
"token" : someCrazyLookingToken
}
exports.loginUser = (req, res) => {
var theResults;
theResults = Picks.getPicks( req , ( err , thePicks) => {
console.log("log this" + JSON.stringify(thePicks));
if (!err){
console.log ("what happened" + err)
}
});
console.log("after the call " + JSON.stringify(theResults));
return res.status(200).json({ picks: thePicks, token: createToken(user) });
}
Why? Is there a timing issue I'm not waiting for in the callback?
It's not necessarily a timing issue, but perhaps a misunderstanding on your part in how callbacks work, particularly in their scope. In your code, theResults is never going to have a value.
Try setting it up like this, and read up on promises and callbacks to get a better understanding on both.
exports.loginUser = async (req, res) => {
try {
const theResults = await new Promise((resolve, reject) => {
Picks.getPicks(req, (err, thePicks) => {
if (err) {
return reject(err);
}
return resolve(thePicks);
})
});
return res.status(200).json({picks: theResults, token: createToken(user)});
} catch (err) {
//handle err
}
}
Code snippet below is using sails waterline ORM to make DB queries and sending response. However, the execution flow is weird, The code outside of map function is running before the map function has finished executing. 'I am outside of map' is being printed in the console before 'I am inside of map'. I think it can be solved this using Promise or async / await. I have tried using Promise.all() below, but it doesn't work, the response is always an empty array. I would be grateful if you could give an example on how to solve this kind of issues.
allMembers: (req, res) => {
const projectId = req.params.id;
ProjectMembers.find({projectId: projectId}).exec( (err, members) => {
if(err) res.serverError("bad request!");
if(members.length === 0) res.notFound({message: "No members are found for this project!"});
let membersInfo = [];
let promise = Promise.all(members.map(m => {
User.findOne({id: m.userId}).exec( (err, user) => {
if(err) membersInfo.push({name: null, userId: null, email:null,rate:null, error: 'Internal error!'})
else if(!user) membersInfo.push({name: null, userId: null, email:null,rate:null, error: 'No user found'})
else membersInfo.push({name: user.name, userId: user.id, rate: m.rate, error: null})
console.log("i am inside of map");
})
}));
console.log("I am outsie of map")
promise.then( (resolve) => {return res.ok({members: membersInfo})});
}
I was about to tell you "don't use queries in .map", but on looking, I think your code is quite close to working. The argument of Promise.all has to be an array of promises. Each User.findOne is indeed a promise - the stumbling block is that once you use .exec it no longer returns a promise.
I think the answer is to do your processing inside the .then instead of right inside the .map:
ProjectMembers.find({projectId: projectId}).exec( (err, members) => {
if(err) return res.serverError("bad request!");
if(members.length === 0) return res.notFound({message: "No members are found for this project!"});
let promise = Promise.all(members.map(m => User.findOne({id: m.userId})));
promise.then( (values) => {
// values is the array of user objects returned from the array of queries
let membersInfo = values.map((user) => {
if (!user) {
return {name: null, userId: null, email:null,rate:null, error: 'No user found'};
} else {
return {name: user.name, userId: user.id, rate: m.rate, error: null};
}
});
return res.ok({members: membersInfo});
}, (err) => {
return res.serverError("Error finding users");
});
The promise only has a single fail callback, so you lose the ability to individually catch and handle querying errors (though you can still individually handle not-found results).
I am trying to build a login API using NodeJS, but my code is not doing what I expect it to. I am very new to js, promises and all so please simplify any answer if possible.
From what I can see in the output of my code, the first promise part does not wait until the function findUsers(...) is finished.
I have a routes file where I want to run a few functions sequentially:
Find if user exist in database
if(1 is true) Hash and salt the inputted password
... etc
The routes file now contains:
var loginM = require('../models/login');
var loginC = require('../controllers/login');
var Promise = require('promise');
module.exports = function(app) {
app.post('/login/', function(req, res, next) {
var promise = new Promise(function (resolve, reject) {
var rows = loginM.findUser(req.body, res);
if (rows.length > 0) {
console.log("Success");
resolve(rows);
} else {
console.log("Failed");
reject(reason);
}
});
promise.then(function(data) {
return new Promise(function (resolve, reject) {
loginC.doSomething(data);
if (success) {
console.log("Success 2");
resolve(data);
} else {
console.log("Failed 2");
reject(reason);
}
});
}, function (reason) {
console.log("error handler second");
});
});
}
And the findUser function contains pooling and a query and is in a models file:
var connection = require('../dbConnection');
var loginC = require('../controllers/login');
function Login() {
var me = this;
var pool = connection.getPool();
me.findUser = function(params, res) {
var username = params.username;
pool.getConnection(function (err, connection) {
console.log("Connection ");
if (err) {
console.log("ERROR 1 ");
res.send({"code": 100, "status": "Error in connection database"});
return;
}
connection.query('select Id, Name, Password from Users ' +
'where Users.Name = ?', [username], function (err, rows) {
connection.release();
if (!err) {
return rows;
} else {
return false;
}
});
//connection.on('error', function (err) {
// res.send({"code": 100, "status": "Error in connection database"});
// return;
//});
});
}
}
module.exports = new Login();
The output i get is:
Server listening on port 3000
Something is happening
error handler second
Connection
So what I want to know about this code is twofold:
Why is the first promise not waiting for findUser to return before proceeding with the if/else and what do I need to change for this to happen?
Why is error handler second outputed but not Failed?
I feel like there is something I am totally misunderstanding about promises.
I am grateful for any answer. Thanks.
Issues with the code
Ok, there are a lot of issues here so first things first.
connection.query('...', function (err, rows) {
connection.release();
if (!err) {
return rows;
} else {
return false;
}
});
This will not work because you are returning data to the caller, which is the database query that calls your callback with err and rows and doesn't care about the return value of your callback.
What you need to do is to call some other function or method when you have the rows or when you don't.
You are calling:
var rows = loginM.findUser(req.body, res);
and you expect to get the rows there, but you won't. What you'll get is undefined and you'll get it quicker than the database query is even started. It works like this:
me.findUser = function(params, res) {
// (1) you save the username in a variable
var username = params.username;
// (2) you pass a function to getConnection method
pool.getConnection(function (err, connection) {
console.log("Connection ");
if (err) {
console.log("ERROR 1 ");
res.send({"code": 100, "status": "Error in connection database"});
return;
}
connection.query('select Id, Name, Password from Users ' +
'where Users.Name = ?', [username], function (err, rows) {
connection.release();
if (!err) {
return rows;
} else {
return false;
}
});
//connection.on('error', function (err) {
// res.send({"code": 100, "status": "Error in connection database"});
// return;
//});
});
// (3) you end a function and implicitly return undefined
}
The pool.getConnection method returns immediately after you pass a function, before the connection to the database is even made. Then, after some time, that function that you passed to that method may get called, but it will be long after you already returned undefined to the code that wanted a value in:
var rows = loginM.findUser(req.body, res);
Instead of returning values from callbacks you need to call some other functions or methods from them (like some callbacks that you need to call, or a method to resolve a promise).
Returning a value is a synchronous concept and will not work for asynchronous code.
How promises should be used
Now, if your function returned a promise:
me.findUser = function(params, res) {
var username = params.username;
return new Promise(function (res, rej) {
pool.getConnection(function (err, connection) {
console.log("Connection ");
if (err) {
rej('db error');
} else {
connection.query('...', [username], function (err, rows) {
connection.release();
if (!err) {
res(rows);
} else {
rej('other error');
}
});
});
});
}
then you'll be able to use it in some other part of your code in a way like this:
app.post('/login/', function(req, res, next) {
var promise = new Promise(function (resolve, reject) {
// rows is a promise now:
var rows = loginM.findUser(req.body, res);
rows.then(function (rowsValue) {
console.log("Success");
resolve(rowsValue);
}).catch(function (err) {
console.log("Failed");
reject(err);
});
});
// ...
Explanation
In summary, if you are running an asynchronous operation - like a database query - then you can't have the value immediately like this:
var value = query();
because the server would need to block waiting for the database before it could execute the assignment - and this is what happens in every language with synchronous, blocking I/O (that's why you need to have threads in those languages so that other things can be done while that thread is blocked).
In Node you can either use a callback function that you pass to the asynchronous function to get called when it has data:
query(function (error, data) {
if (error) {
// we have error
} else {
// we have data
}
});
otherCode();
Or you can get a promise:
var promise = query();
promise.then(function (data) {
// we have data
}).catch(function (error) {
// we have error
});
otherCode();
But in both cases otherCode() will be run immediately after registering your callback or promise handlers, before the query has any data - that is no blocking has to be done.
Summary
The whole idea is that in an asynchronous, non-blocking, single-threaded environment like Node.JS you never do more than one thing at a time - but you can wait for a lot of things. But you don't just wait for something and do nothing while you're waiting, you schedule other things, wait for more things, and eventually you get called back when it's ready.
Actually I wrote a short story on Medium to illustrate that concept: Nonblacking I/O on the planet Asynchronia256/16 - A short story loosely based on uncertain facts.
I don't know if I'm not using the .spread method correctly while working with Bluebird promises on Sails.js models. Here's what I have:
transactionAsync('BEGIN')
.then(function() {
return Model.findOne({ id: 5) });
})
.then(function(results){
if(!results) {
return res.notFound();
}
crypto.randomBytes(24, function(err, buf) {
if(err) throw new Error(err);
var token = buf.toString('hex');
// This is where it fails
return [results, token];
});
})
.spread(function(results, token) {
// It doesn't get to log these
console.log(results, token);
...
})
...
After returning [results, token] on the second .then (inside of the crypto callback), it spits out
[TypeError: expecting an array, a promise or a thenable]
I removed the rest of the code after .spread, since it's not really relevant, and that's where execution stops before returning an error.
I just want to pass variables results and token to the function inside of .spread. What am I doing wrong?
Any help is great. Thanks.
After returning [results, token] on the second .then
That's not what you're doing. You're returning inside of the crypto callback, where it is meaningless. Neither does anybody know of this callback, nor do you actually return anything from the then callback.
The first rule of promise development is to promisify the underlying API so that you can work on pure promises.
var crypto = Promise.promisifyAll(crypto);
// or specifically:
var randomBytes = Promise.promisify(crypto.randomBytes);
Now we can follow rule 3b and actually return a (promise) result from then then callback:
…
.then(function(results) {
if (!results) {
return res.notFound();
}
return crypto.randomBytesAsync(24) // returns a promise now!
// ^^^^^^
.then(function(buf) {
var token = buf.toString('hex');
return [results, token];
});
})
.spread(function(results, token) {
console.log(results, token);
…
})
.then(function(results){
if(!results) {
return res.notFound();
}
crypto.randomBytes(24, function(err, buf) {
if(err) throw new Error(err);
var token = buf.toString('hex');
// This is where it fails
return [results, token];
});
})
is wrong. You are return int from inside the randomBytes callback, not inside the then, so your then simply returns undefined and you then try to .spread that. In order to properly wait for randomBytes, you need to create a promise for that value.
.then(function(results){
if(!results) {
return res.notFound();
}
return new Promise(function(resolve, reject){
crypto.randomBytes(24, function(err, buf) {
if(err) return reject(new Error(err));
var token = buf.toString('hex');
// This is where it fails
resolve([results, token]);
});
});
})