I have a class method (for 'apollo-datasource-rest' that is supposed to fetch a plan from a plan_id. I need to hit two endpoints and combine the data. This works with Promise.all([promise1, promise2]) and then passes the user_ids on to the next Promise.all method that calls the GET /users endpoint multiple times. If I console.log out the usersArray that is returned, I get the array of users, but if I try to return that array, it doesn't get assigned to the objToReturn variable. I also need to add data from snapshot2 to the objToReturn but that is secondary.
getPlanById = async ( planId ) => {
const promise1 = new Promise((resolve) => {
return resolve(this.get('/url1'))
});
const promise2 = new Promise((resolve) => {
return resolve(this.get('/url2'))
});
const objToReturn = await Promise.all([promise1, promise2])
.then(([snapshot1, snapshot2]) => {
return snapshot1.user_ids
})
.then((userIds) => {
this.getUsersFromUserIds(userIds).then((usersArray) => {
console.log(usersArray)
// return usersArray doesn't assign to objToReturn
})
})
return objToReturn
}
getUsersFromUserIds(userIds) {
let userPromises = []
userIds.forEach((uid) => {
const promise = this.get(`/users/${uid}`)
.then((response) => {
if (response.status === 'success') {
return response.data.user
} else {
return null
}
})
userPromises.push(promise)
})
return Promise.all(userPromises).
then((userPromiseData) => {
return userPromiseData
})
}
You need to return the promise of this.getUsersFromUserIds(userIds). So that the promise chain can work.
E.g.
index.js:
class UserAPI {
async getPlanById(planId) {
const promise1 = Promise.resolve({ user_ids: [1, 2] });
const promise2 = Promise.resolve();
const objToReturn = await Promise.all([promise1, promise2])
.then(([snapshot1, snapshot2]) => {
return snapshot1.user_ids;
})
.then((userIds) => {
// You need to return here
return this.getUsersFromUserIds(userIds).then((usersArray) => {
return usersArray;
});
});
return objToReturn;
}
async getUsersFromUserIds(userIds) {
return [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
];
}
}
const userAPI = new UserAPI();
userAPI.getPlanById(1).then((objToReturn) => {
console.log('objToReturn: ', objToReturn);
});
The output in the console:
objToReturn: [ { id: 1, name: 'a' }, { id: 2, name: 'b' } ]
Related
I have a problem querying all data from 1 collection
User-> UidUser-> InfoUser (POST (Collection), name, age ...)
I then used the code below to get the collectionsGroup (Post) but how do I get the User's Name and age?
When I use 2 nested loops, another problem is that forEach cannot use Await and Async inside it makes me lose data.
getData = async () => {
await firebase.firestore().collection('user').doc(Fire.shared.uid).onSnapshot(documentSnapshot => {
firebase.firestore().collectionGroup('Post').where('uid', 'in',
documentSnapshot.data().Followings).get().then(querySnapshot => {
const Post = [];
querySnapshot.forEach(post => {
Post.push({ data: post.data() }) // This is correct and get full data's Post
});
this.setState({ dataPost: Post })
})
})
}
await firebase.firestore().collection('user').doc(Fire.shared.uid).onSnapshot(documentSnapshot => {
firebase.firestore().collectionGroup('Post').where('uid', 'in', documentSnapshot.data().Followings).get().then(querySnapshot => {
const Post = [];
querySnapshot.forEach(async (post) => {
// Incorrect because forEach don't wait callback => Lose a documents's user //
await firebase.firestore().collection('user').doc(post.data().id).onSnapshot(user => {
Post.push({ data: post.data(),user: documentSnapshot.data() })
})
});
this.setState({ dataPost: Post })
})
})
I think promise.all resolve this problem
firebase.firestore()
.collection("user")
.doc(Fire.shared.uid)
.onSnapshot((documentSnapshot) => {
firebase
.firestore()
.collectionGroup("Post")
.where("uid", "in", documentSnapshot.data().Followings)
.get()
.then((querySnapshot) => {
const Post = [];
querySnapshot.forEach((post) => {
Post.push(new Promise((resolve, reject) => {
firebase
.firestore()
.collection("user")
.doc(post.data().id)
.onSnapshot((user) => {
resolve({ data: post.data(), user: documentSnapshot.data() });
});
}))
});
Promise.all(Post).then(res => {
console.log(res)
this.setState({ dataPost: res})
})
});
});
I show how it's work with a simple example with setTimeout function & forEach function maybe it helps others when facing this kind of problems
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
setTimeout(() => {
promises.push(res*2);
}, 10000);
});
console.log(promises);
}
call();
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
setTimeout(() => {
promises.push(res*2);
}, 10000);
});
Promise.all(promises).then((res) => {
console.log("res", res);
});
}
call();
in the above examples, output was an empty array so I figure out a way to fix this issue
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
promises.push(
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res * 2);
}, 3000);
})
);
});
Promise.all(promises).then((res) => {
console.log("res", res);
});
}
call();
if I had an array and a callback function, how do I get the results back asynchronously? here's what I've been trying
const fakeAsync = (list = [], cb = '') => {
let map = {};
list.forEach((x) => {
map[x] = cb(x)
})
return map;
}
const list = [
'user1',
'user2',
'user3'
]
const cb = (name) => {
setTimeout(function(){ return 'good ' + name }, 3000);
}
fakeAsync(list, cb)
it prints out
=> { user1: undefined, user2: undefined, user3: undefined }
I want it to be
=> { user1: 'good user1', user2: 'good user2', user3: 'good user3' }
Calling cb will return nothing directly, so the return inside the setTimeout doesn't actually return anything to be put inside the map object.
You could use Promises to get it simulated like this:
const fakeAsync = (list = [], cb = ()=>{}) => {
Promise.all( // Wait for all Promises to settle/resolve
list.map((x) => { // Make an array of Promises from the list
return new Promise((resolve) => { // Return Promise to be settled
setTimeout(() => { // Simulated delay before resolving with a return value
return resolve(cb(x))
}, 3000)
})
})
).then((map) => { // Get all returned values from the Promises
console.log(map);
});
}
const list = [
'user1',
'user2',
'user3'
]
const cb = (name) => {
return 'good ' + name; // The actual return value
}
fakeAsync(list, cb);
Some references/documentation:
https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developers.google.com/web/fundamentals/primers/promises
How to return resolve() if all the fields is valid otherwise return reject?
let validateData = (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
return new Promise((resolve, reject) => {
fields.forEach(field => {
if (data.hasOwnProperty(field)) {
// resolve if all matched
} else {
//reject?
}
});
});
}
In the main it will have
try {
await validateData(data);
} catch(err) {
throw new Error(`${err}`);
}
If any of the fields do not exist on the data object reject as soon as it is found out. If not you can resolve in the end.
let validateData = (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
return new Promise((resolve, reject) => {
fields.forEach(field => {
if (!data.hasOwnProperty(field)) {
reject();
}
});
resolve();
});
}
return new Promise((resolve, reject) => {
fields.forEach(field => {
if (!data.hasOwnProperty(field)) {
reject();
return;
}
});
resolve();
});
If any of the properties is not valid simply reject else if all of them are valid it wont go inside the if statement and it will resolve the promise.
The error object will contain a description of the validation errors:
let validateData = (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
return new Promise((resolve, reject) => {
let errors = [];
fields.forEach(field => {
if (!data.hasOwnProperty(field)) {
errors.push(`ValidationFieldError: ${field}`);
}
});
errors.length ? reject(new Error(errors.join('\n'))) : resolve();
});
}
If all fields validaton require async operation, you can use Promise.all, which will be resolved only if all the fields validation have been resolved.
E.g.
let validateData = async (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
let fieldsValidatedPromsises = [];
fields.forEach(field => {
let validationPromise = new Promise( function (resolve, reject) {
if (data.hasOwnProperty(field)) {
// resolve if all matched
} else {
//reject?
}
});
fieldsValidatedPromsises.push(validationPromise);
});
return Promise.all(fieldsValidatedPromsises).then(function () {
return true;
})
.catch(function () {
return false;
});
}
let c = await validateData(data);
if (c) {
// All fields validation is successful
}
else {
// One of field validation fails
}
An elegant way would be
let validateData = async (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
let promises = [];
fields.forEach(field =>
promises.push(new Promise((resolve) =>
data.hasOwnProperty(field) ? resolve() : reject())
)
);
return Promise.all(promises);
}
Demo
let validateData = async(data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
let promises = [];
fields.forEach(field =>
promises.push(new Promise((resolve) =>
data.hasOwnProperty(field) ? resolve() : reject()))
);
return Promise.all(promises);
}
async function foo() {
try {
await validateData({
'Field1': 1,
'Field2': 2,
'Field3': 3
});
console.log('valid here');
} catch (err) {
console.log('Invalid');
}
}
foo();
Yet another approach...
let validateData = (data) => {
let fields = [
'Field1',
'Field2',
'Field3'
];
return new Promise((resolve, reject) => {
fields.every(field => data.includes(field)) ? resolve() : reject();
});
}
im tring to push the return value of the resolve to a variable catWithItems which is outside the resolve. inside the resolve the catWithItems works as expected but when i console log catWithItems outside the loop it returns an empty array.
function categoriesSearch(req, res, next) {
let categories = req.batch_categories;
let catWithItems = [];
_.forEach(categories, (category) => {
return new Promise(resolve => {
pos.categoriesSearch(req.tenant, category.id)
.then(item => {
if(item) category.items = item[0];
return category;
})
.then(category => {
catWithItems.push(category);
console.log(catWithItems); //this is works inside here
return resolve(catWithItems);
});
});
});
console.log(catWithItems); //doesn't work returns empty array
res.json({categoryWithItems: catWithItems });
}
this is the pos.categoriesSearch module. it makes a api call to square.(this works as expected)
function categoriesSearch(tenant, category) {
let search_items_url = ${tenant.square.api.v2}/catalog/search,
apiKey = tenant.square.api.key,
payload = {
"object_types": ["ITEM"],
"query": {
"prefix_query": {
"attribute_name": "category_id",
"attribute_prefix": category
}
},
"search_max_page_limit": 1
},
conf = config(search_items_url, apiKey, payload);
return request.postAsync(conf)
.then(items => {
return items.body.objects;
});
}
Your not handling promises right. Try it this way.
function categoriesSearch(req, res, next) {
let categories = req.batch_categories;
let promiseArray = []; // create an array to throw your promises in
let catWithItems = [];
categories.map((category) => {
let promise = new Promise(resolve => {
pos.categoriesSearch(req.tenant, category.id)
.then(item => {
if(item) category.items = item[0];
return category;
})
.then(category => {
catWithItems.push(category);
console.log(catWithItems); //this is works inside here
return resolve(catWithItems);
});
});
promiseArray.push(promise) // add promises to array
});
// resolve all promises in parallel
Promise.all(promiseArray).then((resolved) => {
console.log(resolved);
res.json({categoryWithItems: catWithItems });
})
}
It should be much easier. Not sure if it works, but something to start with:
function categoriesSearch(req, res) {
const categoryWithItems$ = req.batch_categories.map(category =>
pos.categoriesSearch(req.tenant, category.id)
.then(item => ({ ...category, items: item[0] })
);
Promise.all(categoryWithItems$)
.then(categoryWithItems => res.json({ categoryWithItems });
}
I have 2 models defined in Sequelize that are related using a one to many relationship and then used the Sequelize instance to fill the database.
Connection = new Sequelize({...});
Const Recipe = Connection.define('recipe', {name: Sequelize.STRING})
Const Ingredient = Connection.define('ingredient', {name: Sequelize.STRING})
Recipe.hasMany(ingredients);
Ingredient.belongsTo(Recipe);
_.times(3, () => {
return ProteinRecipe.create({
name: `SOMENAME`})
.then((recipe) => {
_.times(3, () => {
return recipe.createIngredient({
name: `INGREDIENT FROM :${recipe.name}`
})
What I would like to do is retrieve all the ingredient data from all of the recipes.
I have tried
const readWithPreferences = (req, res) => {
Recipe.findAll()
.then((recipes) => {
return Promise.all(recipes.map((recipe) => {
let recipeObj = {};
recipeObj.info = recipe.dataValues;
recipeObj.ingredients = [];
recipe.getIngredients()
.then((ingredients)=>{
return Promise.all(ingredients.map((ingredient)=>{
recipeObj.instructions.push(ingredient.dataValues);
}));
});
return recipeObj;
}))
.then((recipesArray) => {
let responseObj = {};
responseObj.data = recipesArray;
res.status(200).send(responseObj);
})
});
}
When I check to see if the data is being accessed in the inner promise call, the logger is showing the data. But I am only receiving the information from the outer promise array. How can I return the data from the inner promise array?
You are not returning the inner promise in the outer Promise.all callback.
const readWithPreferences = (req, res) => {
Recipe.findAll().then(recipes => {
return Promise.all(recipes.map(recipe => {
let recipeObj = { info: recipe.dataValues }
return recipe.getIngredients()
.then(ingredients => {
recipeObj.instructions = ingredients.map(i => i.dataValues)
// now, return the whole recipe obj:
return recipeObj
})
}))
})
.then(data => {
res.status(200).send({ data })
})
}