I have read other related posts and still am not understanding correctly how to use promises.
router.get('/', ensureAuthenticated, function(req, res) {
let promiseToGetResponses = new Promise(function(resolve, reject) {
var indexData = new getIndexData();
resolve(indexData);
console.log('received ' + indexData.length);
});
promiseToGetResponses.then(function(data) {
console.log('then data length ' + data.length);
res.render('index', {rsvpsIn: data});
}).catch(function() { });
});
Console shows this:
received undefined
then data length undefined
returned 1 *** this is from a console.log inside the getIndexData().
The function is getting the data, but my promise usage is not waiting for it.
Thanks.
P.S. I didn't know the getIndexData function was needed. Here it is:
function getIndexData(){
RSVP.find({response: 'in'}, function (err, data) {
if (err) throw err;
// This will be a list of all responses to show in the view
var rsvpsIn = [];
if (data.length > 0) {
// Use because the foreach loop below has async calls.
var responseCounter = data.length;
data.forEach(function(response) {
var foundUser = User.getUserById(response.userId, function(err, user) {
var newRSVP = {userName: user.username, notes: response.notes};
rsvpsIn.push(newRSVP);
// decrement and if we are done, return list
responseCounter -= 1;
if (responseCounter == 0) {
console.log('returned ' + rsvpsIn.length);
return rsvpsIn;
}
});
});
} else {
return rsvpsIn;
}
});
}
Not familiar with RSVP but it appears to be callback based API, so you should wrap it in a promise and just use the promise directly:
function getIndexData(){
return new Promise((resolve, reject) => {
RSVP.find({response: 'in'}, function (err, data) {
if (err) reject(err);
// This will be a list of all responses to show in the view
var rsvpsIn = [];
if (data.length > 0) {
// Use because the foreach loop below has async calls.
var responseCounter = data.length;
data.forEach(function(response) {
var foundUser = User.getUserById(response.userId, function(err, user) {
var newRSVP = {userName: user.username, notes: response.notes};
rsvpsIn.push(newRSVP);
// decrement and if we are done, return list
responseCounter -= 1;
if (responseCounter == 0) {
console.log('returned ' + rsvpsIn.length);
resolve(rsvpsIn);
}
});
});
} else {
resolve(rsvpsIn);
}
});
});
}
then just use it where you use the promise:
router.get('/', ensureAuthenticated, function(req, res) {
getIndexData().then(function(data) {
console.log('then data length ' + data.length);
res.render('index', {rsvpsIn: data});
}).catch(function() { });
});
Related
I have 2 functions... 1st one in auth.js, which does this:
const adminCheck = (req, res) => {
console.log(“one”)
UtilRole.roleCheck(req, res, ‘ADMIN’, (response) => {
if(response) {
return true
} else {
return false
}
})
}
module.exports = {
adminCheck
}
basically checks if the user is an admin in my table. that works, but I am trying to retrieve the boolean in my function in my index.js function, which is below.
router.get(‘/viewRegistration’, auth.ensureAuthenticated, function(req, res, next) {
console.log("authcheck: " + auth.adminCheck())
const user = JSON.parse(req.session.passport.user)
var query = “SELECT * FROM tkwdottawa WHERE email = ‘” + user.emailAddress + “’”;
ibmdb.open(DBCredentials.getDBCredentials(), function (err, conn) {
if (err) return res.send(‘sorry, were unable to establish a connection to the database. Please try again later.’);
conn.query(query, function (err, rows) {
if (err) {
Response.writeHead(404);
}
res.render(‘viewRegistration’,{page_title:“viewRegistration”,data:rows, user});
return conn.close(function () {
console.log(‘closed /viewRegistration’);
});
});
});
})
where I am logging the value in the console.log right under where I initialize the function, it is returning undefined. how can I fix this?
You need to use Promise and wrap all callbacks to actually return a given result value from a function that uses a callback because usually a callback is called asynchronously and simply returning a value from it does not help to catch it in a calling function.
const adminCheck = (req, res) => {
console.log(“one”)
return new Promise(resolve, reject) => {
UtilRole.roleCheck(req, res, ‘ADMIN’, (response) => {
if(response) {
resolve(true)
} else {
resolve(false)
}
})
}
});
then you need to await a result calling this function using await keyword and marking a calling function as async:
router.get(‘/viewRegistration’, auth.ensureAuthenticated, async function(req, res, next) {
console.log("authcheck: " + await auth.adminCheck())
const user = JSON.parse(req.session.passport.user)
var query = “SELECT * FROM tkwdottawa WHERE email = ‘” + user.emailAddress + “’”;
ibmdb.open(DBCredentials.getDBCredentials(), function (err, conn) {
if (err) return res.send(‘sorry, were unable to establish a connection to the database. Please try again later.’);
conn.query(query, function (err, rows) {
if (err) {
Response.writeHead(404);
}
res.render(‘viewRegistration’,{page_title:“viewRegistration”,data:rows, user});
return conn.close(function () {
console.log(‘closed /viewRegistration’);
});
});
});
})
That's simple. You just forgot to return the inner function's return value
const adminCheck = (req, res) => {
console.log(“one”)
return UtilRole.roleCheck(req, res, ‘ADMIN’, (response) => {
if(response) {
return true
} else {
return false
}
})
}
module.exports = {
adminCheck
}
I am trying to store API results into an array.
The data is displayed in console, but on pushing the data into an array, the array is still empty.
Here's the code:
app.post('/fetchFavoriteTweets/', verifyToken, function(req, res) {
var favorites = [];
dbConn.then( function (database) {
var dbo = database.db("twitter_search");
dbo.collection('users').findOne(
{ _id: ObjectId(req.userId) }, function(err, result) {
if(err) throw err;
if(!result.hasOwnProperty('favorite_tweets')) {
res.status(404).json({msg:'record not found'});
}
else {
result.favorite_tweets.forEach(function (tweet) {
T.get('statuses/show', {id: tweet.id}, function(err, data, response) {
if(!err){
favorites.push(data);
console.log(data); //this returns data
} else {
console.log(err);
}
});
});
console.log(favorites);
// res.status(200).json({msg:'success', data:favorites});
}
});
}).catch(function(e){console.log(e)})
});
It looks like you're defining the favorites array within the scope of the function callback. Try putting var favorites = []; above you app.post() call instead.
Also, keep in mind that it will only have a value after the callback is complete, so any synchronous code later down the line will only see the empty array value.
I've updated your code to get favorites by storing separately the promise and call it afterwards:
UPDATE
As you can see in the demo, i have 2x console.log at the bottom, the first one(C1) is contained in the promise favoritesPromise () and the second (C2) is after the promise.
Synchronous actions will never wait for asynchronus actions to take place, therefore in my example C2 will always be outputted before C1, even if console.log(1 ... ) is before console.log(2 ... ), they'll appear reversed in the console.
In the promise i added a setTimeout of 1ms to mock a request, it was all it took to achieve the current output. Another thing you can test is removing the setTimeout then output will change a bit, your promise becomes synchronus until it reaches resolve(favorites), that means favorites has all the data by now, but when resolve takes place, it becomes async, and in your console you will still see C2 first (but now with data) and C1 second.
In my earlier answer i tried to implement this reasoning to your code.
Keep it async folks!
var favorites = [];
var favoritesPromise = () => {
return new Promise((resolve, reject) => {
console.log('Retrieving data from the internet.');
// This timeout mocks your request to anything that is async or promie
setTimeout(() => {
console.log('Request done')
let resultFavorite_tweets = [{
id: 1,
name: 'a dog'
}, {
id: 2,
name: 'a cat'
}];
resultFavorite_tweets.forEach(item => {
favorites.push(item.name);
})
resolve(favorites);
// if you have an error use
// reject(err)
}, 1);
});
}
favoritesPromise().then(favList => {
console.log(1, 'this will always contain data from the internet, but will always be last', favList);
})
console.log(2, 'this will be empty (unless you remove setTimeout), but will always be first', favorites);
app.post('/fetchFavoriteTweets/', verifyToken, function(req, res) {
const favoritesPromise = () => {
return new Promise((resolve, reject) => {
var favorites = [];
dbConn.then(function(database) {
var dbo = database.db("twitter_search");
dbo.collection('users').findOne({
_id: ObjectId(req.userId)
}, function(err, result) {
if (err) reject(err);
if (!result.hasOwnProperty('favorite_tweets')) {
res.status(404).json({
msg: 'record not found'
});
} else {
result.favorite_tweets.forEach(function(tweet) {
T.get('statuses/show', {
id: tweet.id
}, function(err, data, response) {
if (!err) {
favorites.push(data);
console.log(data); //this returns data
} else {
console.log(err);
reject(err);
}
});
resolve(data);
});
console.log(favorites);
// res.status(200).json({msg:'success', data:favorites});
}
});
}).catch(function(e) {
reject(e)
})
});
}
// Here you call the promise to retrieve "favorites"
favoritesPromise().then(favoritesList => {
console.log('your favorites array', favoritesList)
})
})
Try next code
app.post('/fetchFavoriteTweets/', verifyToken, function (req, res) {
var favorites = [];
dbConn.then(function (database) {
var dbo = database.db("twitter_search");
dbo.collection('users').findOne(
{ _id: ObjectId(req.userId) }, function (err, result) {
if (err) throw err;
if (!result.hasOwnProperty('favorite_tweets')) {
res.status(404).json({ msg: 'record not found' });
}
else {
// Counter
let count = result.favorite_tweets.length;
result.favorite_tweets.forEach(function (tweet) {
T.get('statuses/show', { id: tweet.id }, function (err, data, response) {
// Decrease count
count -= 1;
if (!err) {
favorites.push(data);
// Check if count is zero
if (count === 0) {
console.log(favorites);
res.status(200).json({msg:'success', data:favorites});
}
} else {
console.log(err);
}
});
});
}
});
}).catch(function (e) { console.log(e) })
});
I am new in Node.js and I want to pass data array to controller. But I am unable to insert for loop data in array and I also want to get result data out side function.
router.get("/list-group", function(req, res) {
sess = req.session;
var response = {};
if (sess.loginData) {
var TableData = [];
var i = {};
var result = [];
mongoOp.users_group.find({
group_id: req.query.group_id
}, function(e, d) {
var len = d[0].assign_user_id.length;
var assignuserid = d[0].assign_user_id;
for (var i = 0; i < assignuserid.length; i++) {
var assignid = assignuserid[i];
mongoOp.users.find({
_id: assignid
}, function(err, data) {
// This is result array
result[i] = data;
})
}
// And I want to print result array here
console.log(result);
});
} else {
response = {
"error": true,
"message": "Invalid Login"
};
res.json(response);
}
})
I would make use of async and await
router.get('route', (req, res) => {
// ...
users.find(query, (err, d) => {
try {
// ...
var results = []
for (var data of array) {
const result = await fetchUser(data)
results.push(result)
}
console.log(results)
} catch (err) {
console.log('some error occured', err)
}
})
})
async function fetchUser(id) {
return new Promise((resolve, reject) => {
users.find({ _id: id }, (err, data) => {
return err ? reject(err) : resolve(data)
})
})
}
If you're not that familiar with async and await I would recommend this video
u need read about async and callbacks in javascript. An alternative is read about async and await.
I am writing an service, where I retrieve a list of items from a another service, then iterate over result performing keystone.list operation(s).
I am loosing the return status in the find/exec operation. I have tried promises, async, etc.
If someone could point out the correct way to implement this, I would appreciate it.
general implementation:
exports = module.exports = function (req, res) {
var rtn = {
added: 0,
count: 0
}
service(params)
.then(function(svcResult) {
svcResult.forEach(function(item) {
rtn.count++; // <-- correctly seen in apiresponse
Artifact.model.find()
.where({ artifactId: item.id})
.exec(function(err, result) {
if (result.length == 0) {
result = new Artifact.model({
... populate from item ....
});
result.save();
rtn.added++; // <-- not seen in api response
});
});
res.apiResponse(rtn);
});
}
for starters, exec is an async call, which you are ignoring in your res.apiResponse and thus count is incremented and not added, to make life easy, I am moving the exec call outside and wrapping it with promise:
function pExec(id){
return new Promise(function(resolve, reject){
Artifact.model.find()
.where({ artifactId: id})
.exec(function(err, result){
console.log('result: ', result); // there is a possibility that this is not empty array, which seems to be the only case when you increment added value
err? reject(err): resolve(result);
});
});
}
exports = module.exports = function(req, res){ // I think it is 'exports' not 'exposts'
service(params)
.then(function(svcResult) {
var promises = svcResult.map(function(item){
rtn.count++;
return pExec(item.id).then(function(result){
if (result.length == 0) {
result = new Artifact.model({
//... populate from item ....
});
result.save(); // again this might be an async call whose response you might need before incrementing added...
rtn.added++; // <-- not seen in api response
};
});
});
Promise.all(promises).then(function(){
res.apiResponse(rtn);
});
});
}
Thanks... Here is what I have come up with so far....
function getArtifact(id) {
return new Promise(function (resolve, reject) {
Artifact.model.findOne()
.where({artifactId: id})
.exec(function (err, artifact) {
err ? resolve(null) : resolve(artifact);
});
});
}
function createArtifact(item) {
return new Promise(function (resolve, reject) {
var artifact = new Artifact.model({
// ... populate from item ....
});
artifact.save(function (err, artifact) {
err ? resolve(null) : resolve(artifact);
});
});
}
exports = module.exports = function (req, res) {
var rtn = {
success: false,
count: 0,
section: '',
globalLibrary: {
Added: 0,
Matched: 0
},
messages: [],
};
if (!req.user || !req.user._id) {
rtn.messages.push("Requires Authentication");
return res.apiResponse(rtn);
}
if (!req.params.section) {
rtn.messages.push("Invalid parameters");
return res.apiResponse(rtn);
}
var userId = req.user._id;
var section = req.params.section;
rtn.section = section;
service(section)
.then(function (svcResult) {
if (svcResult.length == 0 || svcResult.items.length == 0) {
rtn.messages.push("Retrieved empty collection");
return;
}
rtn.messages.push("Retrieved collection");
var artifacts = svcResult.items(function (item) {
rtn.count++;
return getArtifact(item.objectid)
.then(function (artifact) {
if (!artifact || artifact.length == 0) {
rtn.messages.push("Global Library Adding: " + item.name['$t']);
rtn.globalLibrary.Added++;
artifact = createArtifact(item);
} else {
rtn.globalLibrary.Matched++;
}
return artifact;
})
});
Promise.all(artifacts)
.then(function () {
rtn.success = true;
res.apiResponse(rtn);
});
});
}
I have been trying to wrap my head around Promises. For basic concepts I understand, but once it gets nested, I am a little bit confused. Any feedback is appreciated
Here is the code that I am trying to refactor into Promises (bluebird)
var getIndividualData = function(url, doneGetIndividualData) {
var $, data;
request(url, function(err, res, body) {
if (!err && res.statusCode === 200) {
$ = cheerio.load(body);
data = {
title: $("#itemTitle").children()["0"].next.data,
condition: $("#vi-itm-cond").text(),
price: $("#prcIsum_bidPrice").text(),
imgUrl: $("#icImg")[0].attribs.src,
createdAt: chance.date(),
likes: chance.integer({min: 0, max: 1000})
};
doneGetIndividualData(null, data);
} else {
doneGetIndividualData(err);
}
});
};
var getListing = function(url, doneGetListing) {
var $;
var links = [];
request(url, function(err, res, body) {
if (!err && res.statusCode === 200) {
$ = cheerio.load(body);
$('.vip').each(function(i, el) {
if (i < 15) {
links.push(el.attribs.href);
}
});
async
.concat(links, getIndividualData, function(err, result) {
return doneGetListing(null, result);
});
} else {
doneGetListing(err);
}
});
};
var putToMongo = function(err, result) {
if (devConfig.seedDB) {
mongoose.connect(devConfig.mongo.uri);
Item.find({}).remove(function(err, items) {
Item.create(result, function(err, items) {
console.log('done');
process.kill();
});
});
}
};
async
.concat(urls, getListing, putToMongo);
The first thing to do is wrap request in something that returns a promise. Many promise libraries have utilities for "promisifying" async functions, but I don't think that'll work here because request passes two success values into its callback:
var requestAsync = function(url) {
return new Promise(function (resolve, reject) {
request(function (err, res, body) {
if (err) {
reject(err);
}
resolve({ res: res, body: body});
});
});
};
Once that's done, it gets a lot easier:
var getIndividualData = function(url) {
return requestAsync(url).then(function (result) {
if (result.res.statusCode === 200) {
var $ = cheerio.load(result.body);
return {
title: $("#itemTitle").children()["0"].next.data,
condition: $("#vi-itm-cond").text(),
price: $("#prcIsum_bidPrice").text(),
imgUrl: $("#icImg")[0].attribs.src,
createdAt: chance.date(),
likes: chance.integer({min: 0, max: 1000})
};
}
throw new Error("Individual data status code: " + result.res.statusCode);
});
};
var getListing = function(url, doneGetListing) {
return requestAsync(url).then(function (result) {
if (result.res.statusCode === 200) {
var $ = cheerio.load(result.body),
promises = $('.vip').filter(function (i) {
return i < 15;
}).map(function (i, el) {
return getIndividualData(el.attribs.href);
});
return Promise.all(promises);
}
throw new Error("Listing status code: " + result.res.statusCode);
});
};
var putToMongo = function(result) {
if (devConfig.seedDB) {
mongoose.connect(devConfig.mongo.uri);
Item.find({}).remove(function(err, items) {
Item.create(result, function(err, items) {
console.log('done');
process.kill();
});
});
}
};
Promise.all(urls.map(getListing))
.then(putToMongo)
.catch(function (err) {
// handle error
});