This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I have been trying to create a JSON object with the data that I pull from MongoDB database.
Last line res.status(200).json(userData) seems to return a response before
the data processing is over, so I get an empty object without processed data as a response. Any ideas on how to solve this problem?
// chats are defined before, but excluded here to make a code easier to read
let userData = {};
chats.forEach(function(chat){
let chatId = chat.id;
let userIds = chat['userIds'];
UserAccountingData.find({userId: {$in : userIds}}, function(err, userAccountingData){
if(err){
console.log(err);
res.status(404).json('User data not found.');
return;
} else {
userAccountingData.forEach(function(data){
console.log({
imageUrl: data.imageUrl,
firstName: data.firstName,
lastName: data.lastName
});
userData[data.userId] = {
imageUrl: data.imageUrl,
firstName: data.firstName,
lastName: data.lastName
};
});
}
});
});
res.status(200).json(userData);
Console.log shows that there is a data coming from the database:
{ imageUrl: 'www.test.de', firstName: 'Fender', lastName: 'Fen' }
{ imageUrl: 'www.test.de', firstName: 'Baveler', lastName: 'Bav' }
Thank you for your time
This is because the UserAccountingData.find runs asynchronously. So we need to add async/await logic to this code.
First, define the find function.
const findUserAccountingData = (userIds) => new Promise((resolve, reject) => {
UserAccountingData.find({ userId: { $in: userIds } }, function (err, userAccountingData) {
if (err) {
return reject(err);
}
resolve(userAccountingData)
})
});
Next, modify the original code like below.
let userData = {};
try {
for (let chat of chats) {
let chatId = chat.id;
let userIds = chat['userIds'];
const userAccountingData = await findUserAccountingData(userIds)
userAccountingData.forEach(function (data) {
console.log({
imageUrl: data.imageUrl,
firstName: data.firstName,
lastName: data.lastName
});
userData[data.userId] = {
imageUrl: data.imageUrl,
firstName: data.firstName,
lastName: data.lastName
};
});
}
} catch (error) {
console.log(error);
// res.status(404).json('User data not found.'); // error doesn't occur when user data not exists.
res.status(500).json(JSON.stringify(error));
}
res.status(200).json(userData);
Finally you need to mark the calling function as async.
Related
I have a function that register a client. The function takes care of creating the user, the users company, and adding some meta data about the user. However, the fragile thing in the function is that if one of the requests fails, the user is stuck and can't sign up again - I was told that I could solve this by using a transactions or batch. I've looked into writeBatch() and batches, but it seems you need to have a reference to a document that you want to update. However, in my case, I'm creating new documents - How can i refactor this into be a transaction/batches? What I have so far is this:
const registerUser = async (user: RegisterUserData) => {
try {
const batch = writeBatch(db);
const test = await runTransaction(db, async (transaction) => {
const userDoc = await createUserWithEmailAndPassword(auth, user.email, user.password);
const userId = userDoc.user.uid;
const companyRef = batch.set(doc(db, 'companies'), {
title: user.companyName,
userCount: 1
});
let isAdmin = false;
if (await setUserAsAdmin(userId)) {
isAdmin = true;
}
batch.set(doc(db, 'users'), {
email: user.email,
userId: userId,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: isAdmin,
role: isAdmin ? 'admin' : 'coach',
clientCount: 0,
// companyId: companyRef.id
});
await batch.commit();
});
console.log('test', test)
} catch (e) {
console.log('error', e);
throw new Error('cant register user: ' + e);
}
};
The original function before my refactor looks like this (And is working):
const registerUser = async (user: RegisterUserData) => {
try {
const createUser = await createUserWithEmailAndPassword(auth, user.email, user.password);
const createdCompany = await createUserCompany({
title: user.companyName,
userCount: 1
});
let isAdmin = false;
const adminSet = await setUserAsAdmin(createUser.user.uid);
if (adminSet) {
isAdmin = true;
}
await createUserMetaData({
email: user.email,
userId: createUser.user.uid,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: isAdmin,
role: isAdmin ? 'admin' : 'coach',
clientCount: 0,
companyId: createdCompany.id
});
return createUser;
} catch (e) {
throw new Error('cant register user' + e);
}
};
You cannot include a call to the createUserWithEmailAndPassword() method from the Auth part of the JS SDK in a Firestore transaction. As a matter of fact, the set of atomic operations that you can include in a Firestore transaction can only be Firestore operations (see the doc for the exact list).
So while you can include your two Firestore writes in a batched write it is not possible to include the user creation in the transaction.
One possible solution if you want to avoid the (rare) case where the user is created but not the Firestore docs is to regularly check for this case, for example with a scheduled Cloud Function which corrects the problem.
I'm trying to do a query based on some data but apparently, $nin is not reading the returned values from the function.
My goal is to not show all the users I've followed on the recommended section where there is a get request to receive all the users registered on the db.
I do have the following users on the function, however when I try to do the query, its not working and I've tried for hours fixing that problem.
At username: { $nin: [allFollowers.forEach((following) => following.username)] => Doing this, doesnt work, however when I put strings from the list of following users such as 'user1', 'user2', it works. My api updates on the client and I dont see those two users I follow on the recommended section.
I'd appreciate if you could help me.
exports.recommendedUsers = async function (req, res) {
// all following users in an array
const { followingUsers } = req.body;
/* console.log(followingUsers) =>
[
{
username: 'user1',
avatar: '//www.gravatar.com/avatar/c76fa83b3saddasdas2c04a59d6e063918badbf53?s=200&r=pg&d=mm'
},
{
username: 'user2',
avatar: '//www.gravatar.com/avatar/3758e369b058b393541asdasda4d0e8a1d57402?s=200&r=pg&d=mm'
},
{
username: 'uiser3',
avatar: 'https://static-cdn.jtvnw.net/jtv_user_pictures/bobross-profile_image-0b9dd16cascad7a9bb16b5-70x70.jpeg'
},
{
username: 'user4',
avatar: 'https://static-cdn.jtvnw.net/jtv_user_pictures/82b63a01-628f-4c81-9b05-dd3a501asdasd1fdda-profile_image-70x70.png'
},
{
username: 'user5',
avatar: '//www.gravatar.com/avatar/93cd495a412a1b2asdadabe9b9c72bc246e271?s=200&r=pg&d=mm'
}
] */
let allFollowers = [];
let following = req.body.followingUsers.forEach((follow) =>
allFollowers.push(JSON.stringify(follow.username))
);
console.log(`this is all followers: ${allFollowers}`);
try {
const user = User.find(
{
_id: { $ne: req.user.id },
username: {
$nin: [allFollowers.forEach((following) => following.username)], // not working
},
},
function (err, users) {
let userMap = {};
users.forEach(function (user) {
userMap[user._id] = user;
});
const arrayData = Object.values(userMap);
return res.json(arrayData);
}
).select('-password');
} catch (e) {
console.log(e.message);
}
};
You are using foreach function, that is wrong:
username: {
$nin: [allFollowers.forEach((following) => following.username)],
}
The return value of foreach is undefined, use map function.
try {
const user = User.find(
{
_id: { $ne: req.user.id },
username: {
$nin: allFollowers.map((following) => following.username),
},
},
function (err, users) {
let userMap = {};
users.forEach(function (user) {
userMap[user._id] = user;
});
const arrayData = Object.values(userMap);
return res.json(arrayData);
}
).select('-password');
} catch (e) {
console.log(e.message);
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Is there a way to get the contents of variable data outside the function. I am so stuck, looked various forums in stackoverflow and but failed. I am trying to retrieve data from mongoose model and push those results into an array results and when i print results array, i get empty array .
var results = []
Model.find({firstName:name}, function(err,data){
if(err)
throw err;
data.forEach(function(element) {
console.log(element);
results.push(element);
});
});
console.log(results) --> []
But when i try to print the data inside the ForEach, I am was get the results, listed below.
0 => { _id: 5dc9953a2168993711903698,
id: 763,
firstName: 'Deepak',
lastName: 'Kalra',
image_id: 'No',
logged: false,
__v: 0
}
1 => {
_id: 5dc995546f0f88372080ea36,
id: 511,
firstName: 'Deepak',
lastName: 'Kalra',
image_id: 'No',
logged: false,
__v: 0
}
Entire code
alexa.intent("FirstName", {
"slots": { "name": "AMAZON.FIRST_NAME" },
"utterances": [
"sure {-|name}","{-|name}","my name is {-|name}"
]
},
function(request, response) {
var name = 'Deepak';
try {
var results = await Model.find({firstName:name});
console.log(results)
} catch (error) {
// Handle error.
}
// Model.find({firstName:name}, function(err,data){
// if(err)
// throw err;
// data.forEach(function(element) {
// console.log(element);
// results.push(element);
// });
// });
console.log(results);
});
Is there any solution to fix it. please help me
Because, console.log(results) executed before Model.find was finished.
Two things you can do here:
Put console.log(results) inside the callback.
Use async/await to get similar behaviour.
Example (callback):
Model.find({firstName:name}, function(err,data){
if(err)
throw err;
console.log(data); // data is already an array
});
Example (async/await):
try {
var results = await Model.find({ firstName: name });
console.log(results)
} catch (error) {
// Handle error.
}
Model.find already returns an array of document, so you don't have to run a loop to push them into an array.
UPDATED
alexa.intent("FirstName", {
"slots": { "name": "AMAZON.FIRST_NAME" },
"utterances": [
"sure {-|name}", "{-|name}", "my name is {-|name}"
]
},
async function (request, response) {
var name = 'Deepak';
try {
var results = await Model.find({ firstName: name });
console.log(results)
} catch (error) {
// Handle error.
}
});
Notice the async in front of function.
so I have an array from another function that passes res which is a list looking like this:
[ RowDataPacket { UserID: 26 }, RowDataPacker { UserID: 4 } ]
it stores user id's, what I want is a function that finds the user id's username, and stores them in another array. This is what I have:
function getThem(res, params) {
var promises = res.map(function (item) { // return array of promises
// return the promise:
for (i = 0; i < Object.keys(res).length; i++) {
console.log("user: ", res[i].UserId);
getUsernameFromId(res[users.length].UserId).then(function() {
console.log("username: ", res[0].username);
users.push(res[0].username);
});
}
}, function (err) {
console.error(err);
});
Promise.all(promises).then(function () {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
output in console:
user: 26
user: 4
user: 26
user: 4
users: []
username: undefined
username: undefined
username: undefined
username: undefined
so how can I wait for the for loop to complete the mysql call? Maybe there is another way of doing this?
edit: don't mind the undefined usernames, it's easy to fix later. Just tell me how I can have those undefined inside an array
Assuming (have to assume, because your code seems to use res like a majick object that has everything you need before you do anything with it) the actual res looks like
[ { UserID: 26 }, { UserID: 4 } ]
and getUsernameFromId returns an object with a username property, like
{ username: 'blah', ...otherproperties }
getThem can be simply
function getThem(res, params) {
return Promise.all(res.map(({UserID}) => getUsernameFromId(UserId).then(({username}) => username)))
.then(users => {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
or in "old school" javascript
function getThem(res, params) {
return Promise.all(res.map(function (_ref) {
var UserID = _ref.UserID;
return getUsernameFromId(UserId).then(function (_ref2) {
var username = _ref2.username;
return username;
});
})).then(function (users) {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
I am writing an API in NodeJS in which I use Mongoose and BlueBird. Regarding promise chain, my data was supposed to go through waterfall functions but it didn't. Let my example start with getTagNames to get some JSON , feeding data to retrieveTag to query and end up with res.json().
exports.getTagValues = function (req, res) {
var userId = req.params.uid;
getTagNames(req, res)
.then(retrieveTag)
.then(function (data) {
console.log('tags', data);
res.json(200, data);
})
.catch(function(err){
console.log('err', err);
//handle Error
})
}
Here is my toy data,
function getTagNames(req, res) {
var userId = req.params.uid;
return new Promise.resolve({
'userId': userId,
'variables': [
{ id: 1, name: 'hotel', type: 'String' },
{ id: 2, name: 'location', type: 'String' }
],
})
}
The way I query data. After querying inside mongo, I check whether or not have a document with userID. In case not, insert and return document. Note Tag is my mongo model
function retrieveTag(data){
Tag.findOne({'userId': data.userId})
.exec()
.then( function(tag){
if (tag) {
console.log('result', tag);
// do something ...
return tag;
}
else {
var newTag = new Tag({
advertiserId: advertiserId,
variables: variables
});
newTag.save()
.then(function () {
console.log('newTag', newTag);
return newTag;
});
}
})
}
Here is my result (userId is 1), my expectation is console.log('tags', data); occurs after all then data should not be undefined
tags undefined
GET /api/tag/values/1 200 3ms
newTag { __v: 0,
userId: '1',
_id: 581b96090e5916cf3f5112fe,
variables:
[ { type: 'String', name: 'hotel', id: 1 },
{ type: 'String', name: 'location', id: 2 } ] }
My question is how can I fix it. If there's some unclear, please help me correct.
The explanation is a bit unclear, but if I follow you right you loose data in the promise resolvement chain.
When reading your code, I notice that retrieveTag does not return the Mongoose promise. To let .then in getTagValues use the data found in retrieveTag.
So change to this:
function retrieveTag(data){
return Tag.findOne({'userId': data.userId})
.exec()
.then( function(tag){
...
})
}