Using callback function inside array.map javascript - javascript

I am trying to bcrypt password for every user in an array.
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
bcrypt.genSalt(10)
.then((salt) => {
return item
})
})
Promise.all(promises)
.then((results) => {
console.log(results)
res.json({
"data": results
})
})
})//end route
But I am getting results = [undefined,undefined].
How can I return array element from bcrypt.genSalt(10).then
Please help as I am new to ES6
EDIT: My user users array is like this:
[{ "username": "admin", "admin": true}
]

Simply return the promise from bcrypt.genSalt.
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
return bcrypt.genSalt(10)
.then((salt) => {
return item
})
})
Promise.all(promises)
.then((results) => {
console.log(results)
res.json({
"data": results
})
})
})//end route

When you add .then() after any promise it will directly get resolved. In your code users.map() will run synchronously and the promises will have undefined.
Here is the code you can use :
router.post("/insertuser", (req, res) => {
var promises = users.map((item) => {
return bcrypt.genSalt(10);
})
Promise.all(promises)
.then((results) => {
console.log(results)
});
})//
Also notice that salt is used to generate hash. You are only generating salt. To generate hash of password also add bcrypt.hash(password,salt). Here is the code :
var promises = users.map((item) => {
return bcrypt.genSalt(10);
})
Promise.all(promises)
.then((results) => {
promises = results.map((item, index) => {
return bcrypt.hash(users[index], item);
});
return Promise.all(promises);
})
.then(result => {
console.log(result);
})
.catch(err => {
console.log(err);
});

Related

How to loop thorugh mongoDB with a promise and a forEach loop? I want to populate an Array

I am trying to loop through elements in my MongoDB database and save the values to an Array.
However, after hours of struggling I can't get it done.
Here's my code:
//shopController.js
const Product = require('../models/product');
const User = require('../models/user');
exports.getCart = (req, res, next) => {
const productIds = [];
const productsArr = [];
let userData = null;
User.findById(req.user._id)
.then(user => {
userData = user;
userData.cart.items.map(prodId => {
productIds.push(prodId.productId);
})
console.log(productIds); // [ 5dc1b6ace13a97588620d6c6, 5dc1b6ace13a97588620d6c6 ]
return productIds;
})
.then(prodIds => {
prodIds.forEach(prodId => {
Product.findById(prodId)
.then(product => {
productsArr.push(product);
})
.catch(err => console.log(err))
})
console.log(productsArr); // []
})
.catch((err) => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
}
Don't mind the first output, it's intended to display the same ID twice.
The result when I am logging the productsArr[] is always an empty Array, except i place the console.log(productsArr); inside the forEach() loop, which i don't want because it gets logged too often and I can't render an EJS page like this.
The render function would look like this:
res.render('shop/cart', {
path: '/cart',
docTitle: 'Cart',
products: productsArr,
userData: userData
});
I can't get the products into the productsArr[], as soon as i try to access the productsArr[] outside of the forEach() loop I got an empty Array, so I don't know how to go about this.
Do you have any advice?
Thanks in advance!
You need to render it after the Promises all resolve using Promise.prototype.all, there's no other way to get the populated productsArr array.
Try this:
//shopController.js
const Product = require('../models/product');
const User = require('../models/user');
exports.getCart = (req, res, next) => {
const productIds = [];
// const productsArr = []; not needed anymore
let userData = null;
User.findById(req.user._id)
.then(user => {
userData = user;
userData.cart.items.map(prodId => {
productIds.push(prodId.productId);
})
console.log(productIds); // [ 5dc1b6ace13a97588620d6c6, 5dc1b6ace13a97588620d6c6 ]
return productIds;
})
.then(prodIds => {
Promise.all(prodIds.map(prodId =>
Product.findById(prodId)
).then(productsArr => {
res.render('shop/cart', {
path: '/cart',
docTitle: 'Cart',
products: productArr,
userData: userData
});
console.log(productsArr); // [ ... products ... ]
}).catch(err => {
console.log(err);
})
})
.catch((err) => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
}
You are not waiting for your product promises to be finished, to display your data. You can use the Promise.all([]) function to wait for multiple promises at once.
.then(prodIds => {
Promise.all(prodIds.map(prodId => {
return Product.findById(prodId)
.then(product => {
productsArr.push(product);
})
.catch(err => console.log(err))
})).then(() => {
console.log(
})
Here we are mapping every product ids to a promise (Product.findById) and then passing all of them into the Promise.all() function, which takes an array.
We then wait for all the promise to be resolve, and call the then function.
This way, you are sure all of your promise are finished before printing your data.

Promise executes then function before previous then execution is completed

I'm try to chain a couple of then functions which execute sequentially, but the last .then() is being executed before the previous is done executing and as a result it sends an empty payload. Following is the snippet:
router.get("/selectedHotels", function(req, res) {
let payload = [];
return collectionRef
.where("isOwner", "==", true)
.get() //fetches owners
.then(snapshot => {
snapshot.forEach(user => {
console.log("User", user);
collectionRef
.doc(user.id)
.collection("venues")
.get() // fetches hotels from owners
.then(snapshot => {
snapshot.forEach(doc => {
if (
doc.data().location.long == req.query.long &&
doc.data().location.lat == req.query.lat
) {
console.log(doc.id, "=>", doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
})
.catch(err => {
console.log("No hotels of this user", err);
});
});
})
.then(() => {
console.log("Payload", payload);
response(res, 200, "Okay", payload, "Selected hotels");
})
.catch(err => {
console.log("Error getting documents", err);
response(res, 404, "Data not found", null, "No data available");
});
});
Any suggestions? Thanks
Your main mistake is that you have a non-promise returning function, forEach, in the middle of your nested promise chain.
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where(...).get()
.then((snapshot)=>{
snapshot.forEach(user => {
// ^^^^^^^^^^^^^^^^^ this means the outer promise doesn't wait for this iteration to finish
// ...
The easiest fix is to map your array of promises, pass them into Promise.all and return them:
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where(...).get()
.then((snapshot)=> {
return Promise.all(snapshot.map(
// ...
return collectionRef.doc(user.id).collection('venues').get()
.then(...)
))
That being said, nesting promises like this is an anti-pattern. A promise chain allows us to propagate values through the then callbacks so there's no need to nest them.
Instead, you should chain them vertically.
Here's an example of how you can do that:
router.get("/selectedHotels", function(req, res) {
return collectionRef
.where("isOwner", "==", true)
.get() //fetches owners
// portion of the chain that fetches hotels from owners
// and propagates it further
.then(snapshot =>
Promise.all(
snapshot.map(user =>
collectionRef
.doc(user.id)
.collection("venues")
.get()
)
)
)
// this portion of the chain has the hotels
// it filters them by the req query params
// then propagates the payload array
// (no need for global array)
.then(snapshot =>
snapshot
.filter(
doc =>
doc.data().location.long == req.query.long &&
doc.data().location.lat == req.query.lat
)
.map(doc => ({ id: doc.id, data: doc.data() }))
)
// this part of the chain has the same payload as you intended
.then(payload => {
console.log("Payload", payload);
response(res, 200, "Okay", payload, "Selected hotels");
})
.catch(err => {
console.log("Error getting documents", err);
response(res, 404, "Data not found", null, "No data available");
});
});
Your using firestore so you need to give all documents to map and you also need to return some values to next then. I hope this will help you to solve your problem.
router.get('/selectedVenues',function(req,res){
return collectionRef.where('isOwner', '==', true).get()
.then(snapshot => {
let venues = [];
snapshot.docs.map(user => {
venues.push(collectionRef.doc(user.id).collection('venues').get());
});
return Promise.all(venues);
}).then(snapshots => {
let payload = [];
snapshots.forEach(venues => {
venues.docs
.filter(doc =>
doc.data().longitude == req.query.lng &&
doc.data().latitude == req.query.lat
)
.map(doc =>
payload.push({
id: doc.id,
data: doc.data()
})
)
});
return payload ;
}).then(payload => {
console.log('Payload', payload);
response(res, 200, "Okay", payload, "Selected hotels");
}).catch(err => {
console.log('Error getting documents', err);
response(res, 404, 'Data not found', null, 'No data available');
});
});
You're not returning a promise from within your first then, so there's no way for the code to know that it should wait for an asynchronous result.
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where('isOwner', '==', true).get() //fetches owners
.then((snapshot)=>{
var userVenuesPromises = [];
snapshot.forEach(user => {
userVenuesPromises.push(collectionRef.doc(user.id).collection('venues').get());
})
return Promise.all(userVenuesPromises);
})
.then((snapshots) => {
snapshots.forEach((snapshot) => {
snapshot.forEach((doc)=> {
if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
console.log(doc.id, '=>', doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
});
return payload;
})
.then((payload) => {
...
In addition to using Promise.all() to ensure all nested loads are done before continuing to the next step, this also removes the nested promise, instead unpacking the values from the snapshots in a an additional step.
When chaining .then with asynchronous work, you need to return the promise you want to resolve before the next .then is executed. Something like this :
return Promise.all(snapshot.map(user => {
console.log("User", user);
return collectionRef.doc(user.id).collection('venues').get() // fetches hotels from owners
.then(snapshot => {
snapshot.forEach((doc)=> {
if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
console.log(doc.id, '=>', doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
}).catch((err)=>{
console.log('No hotels of this user', err);
});
});
)
You can see it in action in this sample snippet :
function asyncStuff() {
return new Promise(resolve => {
setTimeout(() => {
console.log('async')
resolve();
}, 100)
});
}
function doStuff() {
console.log('started');
asyncStuff()
.then(() => {
return Promise.all([0,1,2].map(() => asyncStuff()));
})
.then(() => {
console.log('second then');
})
.then(() => console.log('finished'));
}
doStuff();
And see that without the return it gives your initial behaviour :
function asyncStuff() {
return new Promise(resolve => {
setTimeout(() => {
console.log('async')
resolve();
}, 100)
});
}
function doStuff() {
console.log('started');
asyncStuff()
.then(() => {
Promise.all([0,1,2].map(() => asyncStuff()));
})
.then(() => {
console.log('second then');
})
.then(() => console.log('finished'));
}
doStuff();

Use callback or promise to make async code to work like sync in nodejs

My nodejs api function:
exports.userSignup = (req, res) => {
const home = {
address: req.body.name,
phoneno: req.body.code,
};
Home.create(home)
.then((data) => {
createUser()
// i want to complete the above createUser() function fully then only i need to move to this below then function
.then(() => {
const loginDetails = {
username: 'stackoverflow',
};
User.create(loginDetails)
.then((data) => {
return res.status(200).send(data);
}).catch((err) => {
console.log('error while create schema:', err);
});
});
})
.catch((err) => {
console.log('err:', err);
});
};
My createUser function code:
const createUser = () => {
Home.findAll({
raw: true,
}).then((data) => {
data.forEach((client) => {
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
});
console.log('Postgres schema created');
}).catch((err) => {
console.log(err);
});
});
}).catch((err) => {
console.log('Warning:', err.message);
});
};
createUser();
But this one is working as asynchronous,
How to make this using promise resolve reject or callback?
See my code, i made a comment which needs to work first,
I tried with async await but not working!
In order for promise chaining to work, you have to return a promise for any asynchronous functions or else it won't wait. You're also making another asynchronous call for each client you iterate through. In order to deal with multiple promises at once, you need to push each promise into an array and pass it to Promise.all. Here's a snippet that should work.
const createUser = () => {
return Home.findAll({
raw: true,
}).then((data) => {
const promises = [];
data.forEach((client) => {
promises.push(
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
console.log('Postgres schema created');
})
})
);
});
return Promise.all(promises);
}).catch((err) => {
console.log('Warning:', err.message);
});
};
The problem lies in the synchronous data.forEach() call inside the createUser function which doesn't wait for the async createSchema calls to finish before returning.
You can fix this by using an async implementation of forEach function or by using Promise.all()
Here's a piece of code that might work for you:
const createUser = () => {
return Home.findAll({
raw: true
}).then((data) => Promise.all(
data.map((client) => postgresDB.createSchema(client.code).then(() =>
Promise.all(Object.keys(postgresDB.models).map((currentItem) =>
postgresDB.models[currentItem].schema(client.code).sync()
))
))
))
.catch((err) => {
console.log('Warning:', err.message);
});
};
If async/await doesn't quite work for you, just use promise chaining. It's super painful to see people just write callback based code with promises and forget that you can chain promises by returning a promise from the onFulfilled() parameter to then():
const createUser = () => {
return Home.findAll({ raw: true }).
then(data => {
return Promise.all(data.map(client => postgresDB.createSchema(client.code)));
}).
then(() => {
const keys = Object.keys(postgresDBModels);
return Promise.all(keys.map(currentItem => {
return postgresDB.models[currentItem].schema(client.code).sync();
}));
}).
then(() => console.log('Postgres schema created')).
catch((err) => {
console.log(err);
});
};
createUser();
You can use async and await in createUser function. You can alter your function by Using promise, We can able to either resolve or reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

How return a value after a forEach loop using Promises?

I need to know how to return a value after a forEach loop using Promises. In this moment, when I launch my main, I get :
[ Promise { <pending> }, Promise { <pending> } ]
(my sampleidlist contains only 2 records)
This is my code :
MongoClient.connect("mongodb://127.0.0.1/myproject", function(err, db) {
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
// Promisearr is the array of promises where I try to push the promises
var Promisearr = []
// there is the function find_paz that return idPaz for every sampleId in sampleidlist
function find_paz(sampleid) {
// I return a new Promise for every sampleId
// I want to create an array of idPaz
return new Promise((resolve, reject) => {
db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
})
})
}
// Here the forEach loop
sampleidlist.forEach(sampleid => {
Promisearr.push(
find_paz(sampleid)
)
})
Promise.resolve(Promisearr)
.then(Promise.all(Promisearr))
.then(value => {
// value return {promise<pending>}
// I want that value is the array of idPaz
console.log(value)
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
})
Any suggest?
Thank you very much :)
You have it mixed up between Promise.all and Promise.resolve. Here:
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
var Promisearr = []
function find_paz(sampleid) {
return db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
return;
})
})
}
Promise.all(sampleidlist.map(find_paz))
.then(values => {
//values is an array with all the promises resolved
//pazlist should have your data.
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
Give it a try, let me know if you need clarification or if it doesn't work.
You are using Promise.resolve() and Promise.all() the wrong way. You should just call Promise.all() then .then(), like this :
Promise.all(Promisearr).then(value =>
console.log(value)
)

Promises are not behaving as I expect them to

I'm using Express for routing and Sequelize for DB management.
app.get('/api/users/:username', (req, res) => {
let username = req.params.username;
findChattersPerRole()
.then(chattersPerRole => {
console.log('instakbot should\'ve been added by now...');
});
});
The function findChattersPerRole returns an object with each user's username and role as another object.
const findChattersPerRole = () => {
return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
let chattersPerRole = Object.keys(chatters).map(role => {
return chatters[role].map(username => {
console.log('findOrCreateViewer will be executed after this');
findOrCreateViewer(username, role);
return {
username: username,
role: role
};
});
});
return Promise.resolve(flattenDeep(chattersPerRole));
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
The problem is, in my route, I expect the console.log('instakbot should\'ve been added by now...'); to be executed AFTER my viewers got inserted into the database because in my function findChattersPerRole I already insert them with the function findOrCreateViewer. I expect this to happen because in my route I write the console.log when findChattersPerRole() is resolved...
const findOrCreateViewer = (username, role) => {
return Viewer.findOrCreate({
where: {
username
},
defaults: {
instakluiten: 5,
role
}
}).spread((unit, created) => {
console.log('unit is: ', unit.dataValues.username);
if(created){
return `created is ${created}`;
}else{
return unit;
}
});
};
However, in my terminal you can see that this is not the way it's happening... Why aren't my promises being executed at the expected time?
Screenshot of my terminal
The return {username: ...} after findOrCreateViewer(username, role); happens immediately after the function is called and before any data has been inserted. That also means that return Promise.resolve(flattenDeep(chattersPerRole)); happens before any data has been inserted, etc.
You said findOrCreateViewer returns a promise, so you need to wait until that promise is resolved (i.e. wait until after the data was inserted) before continuing with something else.
You want chattersPerRole to be an array of (arrays of) promises and only proceed after all the promises are resolved.
This is easy to do with Promise.all:
const findChattersPerRole = () => {
return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
let chattersPerRole = Object.keys(chatters).map(
role => chatters[role].map(username => {
console.log('findOrCreateViewer will be executed after this');
return findOrCreateViewer(username, role).then(
() => ({username, role})
);
});
);
return Promise.all(flattenDeep(chattersPerRole));
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
Now the promise returned by findChattersPerRole will be resolved after all the promises returned by findOrCreateViewer are resolved.
Promises are doing no magic. Returning a promise doesn't mean that calling the function will block, but rather that you can easily chain callbacks to do something with the result. You'll need to use
function findChattersPerRole() {
return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
let chattersPerRole = Object.keys(chatters).map(role => {
return chatters[role].map(username => {
console.log('findOrCreateViewer will be executed after this');
return findOrCreateViewer(username, role).then(() => {
// ^^^^^^ ^^^^^
return {
username: username,
role: role
};
});
});
});
return Promise.all(flattenDeep(chattersPerRole));
// ^^^ get a promise for an array of results from an array of promises
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
}

Categories

Resources