Retrieving value from Mongoose Promise as a callback - javascript

Having trouble pulling the data from a mongoose promise. I want to find a set of users from mongoose using an appropriate ID and passing the result along a promise chain.
The query is pulling the right data from the database. When I nest the query inside of a promise chain and then attempt to pass along the result, my console.log shows [Function (anonymous)]. Is there an appropriate structure for pulling the data from MongoDB using mongoose and then passing that data along a promise chain?
Promise chain:
addItem(guildId, items, timeCreated) {
return new Promise((resolve, reject) => {
// other functionality omitted for brevity
resolve(guildId, items, timeCreated);
}).then((guild, item, timeCreate) => {
findGroupUsers(guild).then((response) => {
return new Promise((resolve, reject) => {
console.log(response.userId);
resolve(response);
});
});
Mongoose query:
const findGroupUsers = async (guildId) => {
const members = await EngagementTracking.find({ guildId: `${guildId}` }).exec();
console.log(members);
return members;
};
The verified answer successfully pulls the data from the query inside of addItem. To continue and pass the result of the query along chain, I did the following:
addItem(guildId, items, timeCreated) {
return new Promise((resolve, reject) => {
//other functionality
resolve(guildId, items, timeCreated);
}).then((guild, items, timeCreate) => {
return findGroupUsers(guild).then((response) => {
return new Promise((resolve) => { resolve(response); });
});
}).then((data) =>{// other functionality //});

You function add item seem a bit strange you should do this instead:
addItem(guildId, items, timeCreated) {
return new Promise((resolve, reject) => {
// other functionality omitted for brevity
resolve(guildId, items, timeCreated);
}).then((guild, predict, timeCreate) => {
return findGroupUsers(guild).then((response) => {
console.log(response.userId);
return response;
});
you don't need to return a new promise inside a .then
also be warn that you should return your promise (you didn't return findGroupUser)
at the end you should have a addItem.then(/do something/) or an await

Related

Promise data and exception handling

I am confused with the use of promise, specifically of its way of data manipulation (passing values from block to block) and exception handling (bubbling up the error). I am trying to learn a right way to use promise and to handle error, something like
Error: A caught error.
at promiseTwo()
at promiseOne()
at subprocess()
at mainprocess()
Here are my two attempts in implementing them:
Attempt 1: Clumsy, deeply nested, and errors are uncaught.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne().then(data1 => {
// Some code with data1, throw some error
promiseTwo().then(data2 => {
// Some code with data1n2, throw some error
promiseThree().then(data3 => {
// Data manipulation with data1, data2, and data3
return resolve(<...>)
}).catch(err3 => { throw err3 })
}.catch(err2n3 => { throw err2n3 }) // >>> ERR: Cannot get err3.
}.catch(err1n2n3 => { return reject(err1n2n3) }) // >>> ERR: Cannot get err3 or err2.
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// TODO
}).catch(allErr => { return reject(allErr) }
}
Attempt 2: Unable to use data from previous promise block.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne()
.then(data1 => {
// Some code with data1, throw some error
return promiseTwo()
})
.then(data2 => {
// Some code with data1n2, throw some error
// >>> ERR: Cannot get data1
return promiseThree()
})
.then(data3 => {
// Data manipulation with data1, data2, and data3
// >>> ERR: Cannot get data1 and data2
return resolve(<...>)
})
.catch(err1n2n3 => {
return reject(err1n2n3)
})
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// Some code, throw some error
}).catch(allErr => { return reject(allErr) }
}
Note: Some of the promise block (i.e. promiseOne, promiseTwo, etc.) are pre-defined so I do not have control over what data they will return. I am sure there are more errors in the attempts (e.g. if returning a function is a right way to do it).
Please help. Thanks.
for this kind of situation, you can combine promises and async-await together.
From the question, it seems we have three promises and one function that executes and handle them.
You can try something like this -
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
await promiseOne()
await promiseTwo()
await promiseThree()
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
The above code waits for the promises to execute one by one and also catch error from all three promises if any.
And as #samuei mentioned, you can also use Promise.all() in this.
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
const myPromises = [promiseOne, promiseTwo, promiseThree];
const res = await Promise.all(myPromises);
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
And if you don't want to use async-await then you can do something like this as well
const subProcess = () => {
return new Promise((resolve, reject) => {
const myPromises = [];
const myPromises = [promiseOne, promiseTwo, promiseThree];
Promise.all(myPromises)
.then(res => {
// Handle the response
})
.catch(err => {
// Handle the error
})
})
}
It sounds like you're looking for Promise.all, which lets you set a series of promises in motion, then deal with the results when they are all resolved.

Page rendering before Promise resolves in Node, Express, mongoDB

I have removed error handling code as it is working (for simplicicty).
I have a function createOrder() which creates a new record in my MongoDB collection:
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order});
resolve();
});
getOrders() function that adds all current orders in the database to an array.
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
});
resolve();
});
An external javascript file to post an order:
postOrder = () => {
window.location = `/new_order/order=${JSON.stringify(itemArray)}`;
}
and the node application that "gets" the request:
app.get("/new_order/order=:order", (req, res) => {
createOrder(JSON.parse(req.params.order))
.then(getOrders)
.then(_ => res.render("userprofile", {user : curUser, orders: curUserOrders}));
});
when i post a new order the postOrder() function is triggered and "userprofile" is displayed but none of the orders are displayed until i refresh the page again. Im still learning how promises work so im guessing the problem lies there. Please help :).
Promises are used for asynchronous operations, but your example resoves the promise right away.
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order});
resolve();
});
You would write the create function like:
createOrder(order).then(()=>{
// The asynchronous operation is completed!
})
But in your case it would happen the following:
createOrder = order => new Promise((resolve, reject) => {
//Start the asynchronous operation ....
Order.create({orderId: order});
//... but you just resolve the operation right away.
resolve();
});
A better example is probably the other function:
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
});
// You just resolve without waiting for the callback of find.
resolve();
});
What you want:
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
// Here we call resolve inside the callback and we wait for the find method and return the orders to the getOrders().then(orders => {}) call.
resolve(orders);
});
});
A Promise is what the name suggests, it provides you with a promise, that it will be fulfilled (resolved) at some point in the future. So you always want to wait with the resolve function until all operations are completed without errors.
Use it as callback like this
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order}).then(()=>{resolve});
});

Iterate over array of queries and append results to object in JavaScript

I want to return results from two database queries in one object.
function route(start, end) {
return new Promise((resolve, reject) => {
const queries = routeQuery(start, end);
var empty_obj = new Array();
for (i=0; i<queries.length; i++) {
query(queries[i], (err, res) => {
if (err) {
reject('query error', err);
console.log(err);
return;
} else {
empty_obj.push(res.rows);
}});
}
console.log(empty_obj);
resolve({coords: empty_obj});
});
}
This is my code right now, the queries are working fine but for some reason, pushing each result into an empty array does not work. When I console log that empty object, it stays empty. The goal is to resolve the promise with the generated object containing the two query results. I'm using node-postgres for the queries.
Output of res is an object:
{
command: 'SELECT',
rowCount: 18,
oid: null,
rows: [
{ ...
I suggest you turn your query function into a Promise so that you can use Promise.all:
// turn the callback-style asynchronous function into a `Promise`
function queryAsPromise(arg) {
return new Promise((resolve, reject) => {
query(arg, (err, res) => {
if (err) {
console.error(err);
reject(err);
return;
}
resolve(res);
});
});
}
Then, you could do the following in your route function:
function route(start, end) {
const queries = routeQuery(start, end);
// use `Promise.all` to resolve with
// an array of results from queries
return Promise.all(
queries.map(query => queryAsPromise(query))
)
// use `Array.reduce` w/ destructing assignment
// to combine results from queries into a single array
.then(results => results.reduce(
(acc, item) => [...acc, ...item.rows],
[]
))
// return an object with the `coords` property
// that contains the final array
.then(coords => {
return { coords };
});
}
route(1, 10)
.then(result => {
// { coords: [...] }
})
.catch(error => {
// handle errors appropriately
console.error(error);
});
References:
Promise.all - MDN
Array.reduce - MDN
Destructing assignment - MDN
Hope this helps.
The issue you currently face is due to the fact that:
resolve({coords: empty_obj});
Is not inside the callback. So the promise resolves before the query callbacks are called and the rows are pushed to empty_obj. You could move this into the query callback in the following manner:
empty_obj.push(res.rows); // already present
if (empty_obj.length == queries.length) resolve({coords: empty_obj});
This would resolve the promises when all rows are pushed, but leaves you with another issue. Callbacks might not be called in order. Meaning that the resulting order might not match the queries order.
The easiest way to solve this issue is to convert each individual callback to a promise. Then use Promise.all to wait until all promises are resolved. The resulting array will have the data in the same order.
function route(start, end)
const toPromise = queryText => new Promise((resolve, reject) => {
query(queryText, (error, response) => error ? reject(error) : resolve(response));
});
return Promise.all(routeQuery(start, end).map(toPromise))
.then(responses => ({coords: responses.map(response => response.rows)}))
.catch(error => {
console.error(error);
throw error;
});
}

Node.js MongoDB Utilizing promises to make MongoDB method calls

EDIT
So I am creating an e-commerce website and I'm running into a few road blocks. I have since updated my code to make it cleaner and easier to read and I somewhat pinpointed where my issue stems from (though there could be more). Here is the updated code:
StoreDB.prototype.addOrder = function(order) {
console.log('addOrder');
return this.connected.then(function(db) {
console.log(order);
var orders = db.collection('orders');
var products = db.collection('products');
return insert(order, orders, db)
.then(function(updatedOrdersList) {
return obtainOrders(updatedOrdersList);
})
.then(function(orderList) {
return updateProducts(orderList, products);
})
.catch((err) => console.log('There was an issue in the promise chain\n' + err));
});
}
function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
orders.insert(order);
resolve(db.collection('orders'));
});
}
function obtainOrders(orders) {
return new Promise(function(resolve, reject) {
console.log('Obtaining orders');
var orderList = orders.find({}).toArray();
resolve(orderList);
});
}
function updateProducts(orderList, products) {
return new Promise(function(resolve, reject) {
console.log('Getting the products that match the cart')
var promises = [];
var promises2 = [];
console.log('orderList', orderList);
for (var item in orderList[0].cart) {
console.log('item', item);
promises.push(products.find({$and : [{"_id" : item}, {"quantity" : {$gte: 0}}]}).toArray());
}
Promise.all(promises)
.then(() => {
console.log('Updating the cart quantity')
for (var i = 0; i < promises.length; i++) {
console.log(promises);
// console.log(productList[item], productList[item]._id, productList[item].quantity, cart);
promises2.push(products.update({"_id" : productList[item]._id}, {$set :{"quantity" : (productList[item].quantity - cart[productList[item]._id])}}));
promises2.push(products.find({}).toArray());
Promise.all(promises2)
.then(() => promises[promises2.length-1]);
}
});
});
}
It appears that when obtainOrders is called in the promise chain, it returns an empty array. I'm not too sure why it does this because in the chain where:
return insert(order, orders, db)
.then(function(updatedOrdersList) {
return obtainOrders(updatedOrdersList);
})
.then(function(orderList) {
return updateProducts(orderList, products);
})
The function call to obtainOrders from above waits for the method call to Collections.find to be completed before it is resolved (returning an array of orders). Some reason it is returning an empty array.
Any help would be greatly appreciated!
Not sure what the relationship between the first block and second is, but in the first block:
function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
orders.insert(order);
resolve(db.collection('orders'));
});
}
At a glance it seems like orders.insert(order) is asynchronous, but you're not waiting for it to finish before resolving.
You could either make the function asynchronous and
await orders.insert(order);
or
orders.insert(order)
.then(() => resolve(db.collection('orders')))
But doesn't having the method Collections.insert inside a promise make that method call asynchronous?
Yes, Collections.insert is implicitly asynchronous in that it returns a Promise. I was talking about declaring it async explicitly so you can await other async calls in the function body. Note the addition of async at the start and await orders.insert().
async function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
await orders.insert(order);
resolve(db.collection('orders'));
});
}
If you don't await orders.insert (either via await or .then) you're calling insert and immediately returning the db.collection call, which creates a race condition where the new order may not be inserted yet. It's possible the new order could be there but it seems unlikely, and it's not going to be there reliably.
There's also no need to create a new Promise here. db.collection already returns a Promise. Just return the one you already have:
function insert(order, orders, db) {
console.log('Inserting order into orders collection');
return orders.insert(order)
.then(() => db.collection('orders'));
}
Take a look at this sample code snippet. I've mocked out the orders.insert and db.collection methods.
// mock orders.insert
const orders = {
insert: async() => {
return new Promise((resolve) => {
// wait a second then resolve
setTimeout(resolve, 1000);
});
}
}
// mock db.collection
const db = {
collection: async() => {
return new Promise((resolve) => {
// wait a second then resolve with mock data
setTimeout(() => resolve(['foo', 'bar', 'baz']), 1000);
});
}
}
function insert(order, orders, db) {
return orders.insert(order)
.then(() => db.collection('orders'));
}
// same as above, but with async/await
async function insertAsync(order, orders, db) {
await orders.insert(order);
return await db.collection('orders');
}
function go() {
// using then
insert(null, orders, db).then(console.log);
// using async/await;
insertAsync(null, orders, db).then(console.log);
}
go();

Retrieve PromiseResolve data in JavaScript

I am retrieving tab data from Google Chrome, stored in a Promise object that will be fulfilled with an array. The proper data is currently stored in a Promise object, but I want to only access the array itself.
I have tried wrapping the result in a new Promise and trying to resolve it again, but I keep getting the data in a Promise object.
export async function getTabData(){
let tabs = [];
await window.chrome.tabs.query({currentWindow: true}, function(data){
for(let i=0; i<data.length; i++){
tabs.push(data[i]);
};
});
return tabs;
}
export async function getPromise() {
const promise1 = await Promise.resolve(getTabData())
.then(i => i)
new Promise(function(res, rej){
res(promise1);
})
.then(function(result){
return Promise.resolve(promise1);
})
.then(function(result){
console.log(result);
});
}
I expect to get an array of Tabs, but I get a Promise object where that array is stored within the PromiseValue
You can't await the tabs query since it isn't returning a promise. So wrap it in a new promise and resolve it in the callback.
Currently the array is being returned empty and populated after you log it
export function getTabData() {
return new Promise((resolve, reject) => {
window.chrome.tabs.query({currentWindow: true}, resolve);
});
}
export async function doTabsStuff() {
const tabs = await getTabData();
// do something with tabs array
}

Categories