Resolve Promise in mongoose virtual property? - javascript

I'm using asynchronous virtual properties to count how often that document has been referenced in a specific other collection. This feature has been added.
// Schema mortician.js
const Pickup = require('./pickup')
const mongoose = require('mongoose')
const mortSchema = mongoose.Schema({
name: {
type: String,
required: true
}
}, { timestamps: true })
mortSchema.virtual('count').get( async function () {
return await Pickup.countDocuments({ mortician: this._id })
})
module.exports = mongoose.model('Mortician', mortSchema)
However, when I try to render it like this, it's returning a Promise: Promise { <pending> } and the displayed value is [object Promise], like joseym describes over here: Support for Asynchronous Virtual #1894
async index(req, res) {
try {
const morticians = await Mortician.find({}).exec()
res.render('pages/morticians', {
title: 'Bestatter',
page: req.originalUrl,
morticians: morticians
})
} catch (err) { err => console.log(err) }
..
}
Since I'm directly passing the morticians element to render, I've idea where to place the needed await for mortician.count. I want to avoid looping (for (const mortician of morticians)), before passing it to res.render. How to solve this?
Does it even make sense, to query ("OtherSchema".find..) with in an virtual property? What's best practice?

You need to execute your promise, then save the result in a variable which will be used to render it
something like this
async index(req, res) {
try {
const morticians = await Mortician.find({}).exec();
res.render('pages/morticians', {
title: 'Bestatter',
page: req.originalUrl,
morticians: morticians
})
} catch (err) {
err => console.log(err)
}
}

Related

Zone Aware Promise with Ionic 5 and Angular 10

I'm new to Promises (usually use Observables) and Ionic.
I have using capacitor, I have stored the user Id in local storage with this:
import { Plugins } from '#capacitor/core';
async storeAuthData(token: string, user: IUserAuth): Promise<void>{
await Plugins.Storage.set({
key: '_token',
value: token
});
await Plugins.Storage.set({
key: '_u',
value: JSON.stringify(user)
});
}
I want to retrieve the userId from that storage set. I have wrapped my Promise in a return and believe I've done the correct syntax, but when I call this function I get the Zone Aware Promise _state: null info in the console.
async getUserId(): Promise<string> {
return Plugins.Storage
.get({key: '_u'})
.then(userObj => {
var user = JSON.parse(userObj.value);
return user.id.toString();
}).catch(err => {
console.log(err)
return null;
});
}
Any ideas on how to do this?
calling the getUserId func like this:
var id = await this.auth.getUserId();
console.log(id)
One thing about promises is that we have to wrap them in async methods.
So your code would be something like this:
async storeAuthData(token: string, user: IUserAuth): Promise<void> {
// now we introduce the await or the code will run out of control
await Plugins.Storage.set({
key: '_u',
value: JSON.stringify(user)
});
// same here
await Plugins.Storage.set({
key: '_token',
value: token
});
}
Then retrieving it
async getUserId(): Promise <string> {
return Plugins.Storage
.get({
key: '_u'
})
.then(userObj => JSON.parse(userObj.value))
.catch(err => {
console.log(err)
return null;
});
}
And when we are calling we have to use the await keyword too.
Something like this:
console.log('userId', await UserService.getUserId())

Async function returning Promise even after calling Await

I've been trying to create a helper function to return a document's data from the Firebase database using Nodejs:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
}
return targetedDocument.data();
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};
But when I tried to use it, it always returns back a promise:
const documentFinder = require('./helper-function-path');
router.post('post',(req,res)=>{
const requiredDocument = documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument); //returned a promise rather than an object document
})
What am I doing wrong here? Some pointer would be very much appreciated. Thank you.
async functions, by definition, always return a promise. You can't make an asynchronous function to be synchronous simply by wrapping it. The caller will always still have the promise to deal with. You can deal with the returned promise in your express route by making its callback also async, and awaiting the result of the function call:
router.post('post', async (req,res)=>{
const requiredDocument = await documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument);
})
Please try out:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
} else {
return targetedDocument.data();
}
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};

How to access the body of a request outside in an async function?

So I have a post route which is an async await function, and I have a reuest inside it which pulls some data from the api and I want to save the content of that body in a variable outside the request function
I have tried using promises but I am not really familiar with that.
//#route POST api/portfolio/stock
//#desc Create or update a user's stock portfolio
//#access private
router.post(
"/stock",
auth,
[
check("symbol", "symbol is require or incorrect.")
.not()
.isEmpty(),
check("qty", "Quantity of the stock purchased is required")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty) {
return res.status(400).json({ errors: errors.array() });
}
const { stock, qty } = req.body;
const newPortfolio = {};
newPortfolio.user = req.user.id;
newPortfolio.stocks = [];
if(stock) newPortfolio.stocks.stock = stock;
if(qty) newPortfolio.stocks.qty = qty;
request(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${config.get(
"API_KEY")}`, (error, response, body) => {
if (error) console.error(error);
if (response.statusCode !== 200) {
res.status(404).json({msg: 'No stock found'});
}
let content = JSON.parse(body);
let quote = content['Global Quote'];
});
newPortfolio.stocks.stockInfo = quote;
try {
let portfolio = await Portfolio.findOne({ user: user.req.id });
//update if exists
if(portfolio) {
portfolio = await Portfolio.findOneAndUpdate(
{ user: user.req.id },
{ $push: { stocks: newPortfolio.stocks }}
);
return res.json(portfolio);
}
//create if not found
portfolio = new Portfolio(newPortfolio);
await portfolio.save();
res.json(portfolio);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
I want to save myPortfolio.stocks.stockInfo using body of that request.
How to access the body of a request outside in an async function?
You don't. Asynchronous results are only available in the asynchronous context. For an asynchronous function that returns the result via a callback, that means you consume/use the results INSIDE the callback. Any code that needs access to those results goes in the callback. In traditional async Javascript programming, you just continue the code of your function inside the callback.
Fortunately, promises and the invention of async and await can make the coding a bit simpler. For asynchronous functions that return a promise (instead of taking a callback), you can use await to get their result and you can write code that looks more like the sequential model, even though it's still asynchronous.
For example, this is what a rewrite of your function might look like where we switch the request-promise library (same as the request library, but returns a promise instead of uses a callback) and then we use await on the result:
const rp = require('request-promise');
router.post(
"/stock",
auth,
[
check("symbol", "symbol is require or incorrect.")
.not()
.isEmpty(),
check("qty", "Quantity of the stock purchased is required")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty) {
return res.status(400).json({
errors: errors.array()
});
}
const {
stock,
qty
} = req.body;
const newPortfolio = {};
newPortfolio.user = req.user.id;
newPortfolio.stocks = [];
if (stock) newPortfolio.stocks.stock = stock;
if (qty) newPortfolio.stocks.qty = qty;
try {
let body = await rp(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${config.get("API_KEY")}`);
let content = JSON.parse(body);
newPortfolio.stocks.stockInfo = content['Global Quote'];
} catch (e) {
console.error(e);
res.status(404).json({
msg: 'No stock found'
});
return;
}
try {
let portfolio = await Portfolio.findOne({
user: user.req.id
});
//update if exists
if (portfolio) {
portfolio = await Portfolio.findOneAndUpdate({
user: user.req.id
}, {
$push: {
stocks: newPortfolio.stocks
}
});
return res.json(portfolio);
}
//create if not found
portfolio = new Portfolio(newPortfolio);
await portfolio.save();
res.json(portfolio);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
Note: your adding properties to newPortfolio.stocks which is an array is quite unusual. While it's not technically wrong, you would usually declare newPortfolio.stocks to be an object, not an array if you were just going to add properties to it and not use it like an actual array. It can often be confusing to people reading your code if you use the same variable as both an object and an array. Usually, you want a variable (or property) to behave as one or the other, not both.

Return Value in Promise

I know this questions is asked several times in several ways. But I realy dont get it:
I have a promise with a resolved value. I can console.log this object and everything seems to be fine. I can see, what I want to see.
I use PouchDB and NuxtJS (VueJS)
import PouchDB from 'pouchdb'
let testdb = new PouchDB('testdb');
let testDoc = function () {
testdb.get('2').then(function (doc) {
console.log(doc);
}).catch(function (err) {
console.log(err);
});
}
This works great. My result is what I expect and hope to see:
{
text: "testen",
_id: "2",
_rev: "1-c7e7c73d264aa5e6ed6b5cc10df35c5a"
}
Perfect. But now I am struggeling with returning this value, so other functions can access to it. Especially returning this data. In VueJS eg like that:
// ..
export default {
data() {
return {
doc: testDoc
}
}
}
So I can access to it via instance. But ofcourse, If I do it like that, data is promise
data: [
doc: promise
]
But I need the value, not what it is. I dont understand how to return the value.
I have read several How To´s. I guess, I understand the different between Callback and Promise. With both and async functions I get the same result. But all example are always with console.log(). But this works for me.
Has anyone an example hot to access this (scoped or nested?) value?
If I return the data:
let testdb = new PouchDB('testdb');
let testDoc = function () {
testdb.get('2').then(function (doc) {
return doc;
}).catch(function (err) {
console.log(err);
});
}
Why hasnt testDoc the value now? Or where the hack is the value?
I always have done it via commiting the value into the vuex store. This also works great.
let fetchOrga = async function({ store }) {
try {
let orgaDoc = await orgadb.get('orga');
store.commit('orgaUpdate', orgaDoc)
} catch (err) {
console.log(err);
}
}
But like I said, I want to have this data directly under control via IndexedDB
You can use async/await to wait until promise resolve:
// nuxt.js way
async asyncData() {
let testdb = new PouchDB('testdb');
return {
doc: await testdb.get('2'),
};
},
UPD (by comments):
data() {
return {
isReady: false,
doc: null,
};
},
async mounted() {
let testdb = new PouchDB('testdb');
this.doc = await testdb.get('2');
this.isReady = true;
},
In the mount of the component you should update your state doc then your doc will be available to work with anywhere in your inside your component.
export default {
data() {
return {
doc: [],
error : ""
}
},
mounted: () => {
testdb.get('2').then(doc => {
this.doc = doc;
}).catch(function(err) {
this.error = err;
});
}
}
testDoc in your example now contains a function. Not a promise nor the doc you're getting from the promnise. To get the actual doc you need to get inside the promise. Like this
let testdb = new PouchDB('testdb');
testdb.get('2').then(function (doc) {
// Your doc is here
console.log(doc);
return doc;
}).catch(function (err) {
console.log(err);
});
Alternatively you can use the async/await syntax, like this
let doc = await testdb.get('2');
// Your doc is here
console.log(doc);

Why spread() method doesn't work in Sequelize?

I'm using a Sequelize for my node.js app. I use findOrCreate() method to create a new user if not exist. Accordingly to docs findOrCreate returns an array containing the object that was found or created and a boolean that will be true if a new object was created and false if not.
The sequelize recommend to use spread() method which divides the array into its 2 parts and passes them as arguments to the callback function. First part is a user object and second is boolean if new row was added.
I work in async/await style. My code is:
app.post('/signup', async (req, res) => {
try {
let insertedUser = await User.findOrCreate({
where: { email: req.body.userEmail },
defaults: {
pass: req.body.userPass
}
})
insertedUser.spread((user, created) => {
if(created) {
res.json(user.email)
}
})
} catch (err) {
console.log(`ERROR! => ${err.name}: ${err.message}`)
res.status(500).send(err.message)
}
}
})
After post request I get an error:
ERROR! => TypeError: insertedUser.spread is not a function
Why is that, the doc says it must be a function?
spread method does not exist on resolved value from findOrCreate. You have to either chain spread with then of promise returned by findOrCreate. Or you need to chain with findOrCreate directly and use await.
Here is the updated code with await:
app.post('/signup', async (req, res) => {
try {
let created = await User.findOrCreate({
where: { email: req.body.userEmail },
defaults: {
pass: req.body.userPass
}
}).spread((user, created) => {
return created;
})
if(created) {
res.json(user.email)
}
} catch (err) {
console.log(`ERROR! => ${err.name}: ${err.message}`)
res.status(500).send(err.message)
}
}
})

Categories

Resources