async trouble in NodeJS and MongoDB - javascript

i'm trying to create an array which should have an items with open quotation. i have an Items DB in Mongo with key "open_quotation", if there is no quotation for this item => open_quotation: null. i want to map all the array items and to check if there is any open quotations for this item, if there are an open quotation - i want to push it to another array, whihc I created, and to send to client. the problem that when i do map method on items array it is an async function and i get an array already after that the response has been send... so the client receive it as an empty array. pls help to solve this problem.
const{items}= req.body;
const quotations=[];
items.map(async item=>{ const i=await Item.findOne({_id: item._id})
if(i){
`if(i.openquotation){ quotations.push(i)}`
return
)}
res.send({response: true, quotations:quotations})
`````

Can you try that:
try {
const response = await Promise.all(items.map(item => Item.findOne({_id: item._id})));
const quotations = response.filter((elem) => !!elem.openquotation);
res.send({ response: true, quotations });
} catch (error) {
res.send({ response: false, error });
}

you can use async and q module for find query in loop
when you run mainFunction(), mainFunction calls getData()
let async = require('async');
let q = require('q');
const mainFunctio = async()=>{
const{items}= req.body
console.log("start")
resultFromFindLoob = await getData(items)
console.log("finish");
console.log(resultFromFindLoob)
}
const getData = async (items)=>{
let defer =q.defer();
let result = []
async.eachSeries(items , async(item)=>{ // like a for
try {
let i = await Item.findOne({_id: item._id}).lean();
//do something for result (processing)
if(i)
result.push(i)
} catch (error) {
console.log(error)
}
},()=>{ //callback when finish loop
console.log("finish findone() loop")
defer.resolve(result)//return the result
})
return defer.promise
}
mainFunctio()
restult of mainFunction() based on console.log
1.start mainFunction
2.start getData
3.finish find loop getData
4.finish mainFunction
5.final result : .....

Related

FindOne inside map returns no results

I'm trying to do a search using FindOne inside map but it never finds the data by Product Id. I don't understand the reason. Im using express on nodejs.
This is my code:
const calc = (details) => {
let grandSubtotal = 0;
details.map( async detail => {
const {verifyProduct} = await Product.find({ _id: detail._id});
console.log(detail._id);
console.log(verifyProduct); // UNDEFINED
...
Should be:
const result = await Promise.all(details.map( async (detail) => { … } ));
when you do it like you done you will get a pending promise object that never going to be resolved, I don’t know if you want to return some results, if no just do await Promise.all
Also this should be:
const calc = async (details) => { … }
Mongoose find returns a list of results. findOne returns an object.
The code is doing the equivalent of:
const {verifyProduct} = []
Use findOne to get an object to destructure, and test the result before use.
details.map( async (detail) => {
const res = await Product.findOne({ _id: detail._id });
if (!res) {
//throw new Error('No id '.detail._id)
console.log('No id', detail._id)
}
const { verifyProduct } = res
console.log(detail._id);
console.log(verifyProduct);
}
Also (as #antokhio noted), if you want to use the returned result array of the details.map you will need to await those as well.
You don't need await here
Product.find(({ _id }) => _id === detail._id );

MongoDB Queries Async for loop results coming back empty

I have a list of IDs to query Mongo with which I am doing in a for loop and it always returns an empty []. Initially it kept returning promises so I moved from forEach to the standard for loop as you see here and toArray() both of which resulted in an empty [].
async function queryRoles(id) {
const db = await connectToMongo()
let response;
try {
response = await db.collection("permissions").find({"_id": xxx}).toArray();
console.log(await response);
} catch (error) {
console.log(error);
}
return response;
}
async function checkAuthorisation(list, groups) {
for (const item of list) {
const index = list.indexOf(item);
const rolesList = await queryRoles(item._id);
console.log(rolesList);
};
}
The rolesList always comes back in [] and is never populated with anything.
However, if I just update code with the same query for a single mongo document without using the for loop, the expected response comes back, so I know the connectivity to MongoDB is fine.
What am I doing wrong? Pulling my hair out at this point!!
Did you try this approach,
async function checkAuthorisation(list, groups) {
const roleList = await Promise.all(
list.map(async (item, index) =>
await queryRoles(item._id)
)
);
...
}

Movie API:How Can I Return The Value?

I'm using The Movie Database API. And the problem that i can't solve is returning "keys" variable when i call the function with Movie's id.I'm new on JavaScript that's why i can't solve this. Hope someone can help me, thanks in advance.
const APIURL = "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=[MY API KEY HERE]&page-1";
getMovies(APIURL)
async function getMovies(url)
{
const resp = await fetch(url);
const respData = await resp.json();
showMovies(respData.results)
}
async function getTrailer(id)
{
const resp = await fetch(`https://api.themoviedb.org/3/movie/${id}/videos?api_key=[MY API KEY HERE]&language=en-US`);
const respDataa = await resp.json();
let results = respDataa.results;
let keys = results[0].key;
return keys;
}
function showMovies(movies){
movies.forEach(movie => {
const modals = document.createElement('div');
modals.classList.add('modal');
modals.innerHTML = ` <a target="_blank" href ="https://www.youtube.com/watch?v=${getTrailer(movie.id)}">Watch Trailer</a>`
}
}
Well, first of all, always hide your api keys if you're posting your code somewhere (even if it's a private repository, you shouldn't do it).
Secondly, if you want to return multiple keys, you can map the results to extract id from each of them and do a return:
async function getTrailer(id)
{
const resp = await fetch(`https://api.themoviedb.org/3/movie/${id}/videos?api_key=04c35731a5ee918f014970082a0088b1&language=en-US`);
const respDataa = await resp.json();
let results = respDataa.results;
return results.map(({ key }) => key);
}
Async functions return a Promise in JavaScript.
Simply add return keys at the end of your function.
Then you can do:
getTrailer(528085).then(data => {
// do something with the data
})
You can also handle errors:
getTrailer(528085)
.then(data => {
// do something with the data
})
.catch(err => {
/*
An error occured. You can use err.toString() to convert the error into a string
*/
})
If you want to get the returned data immediately from an async function(or a promise), put your logic inside an async function, then you can simply do:
let keys = await getTrailer(528085)
And, here is how to handle errors in async/await:
try {
let keys = await getTrailer(528085)
}
catch(err){
/*
An error occured. You can use err.toString() to convert the error into a string
*/
}
By the way, like Desiigner said, don't keep your API keys in the client. Anyone can see your API key. Use a server to return the API response to the client.
We have to await or.then the return (it’s a Promise).
function showMovies(movies) {
// make the forEach callback async
movies.forEach(async (movie) => {
console.log(getTrailer(movie.id)) // Promise {<pending>}
const trailer = await getTrailer(movie.id);
console.log({ trailer });
const modals = document.createElement("div");
modals.classList.add("modal");
modals.innerHTML = ` <a target="_blank" href ="https://www.youtube.com/watch?v=${trailer}">Watch Trailer</a>`;
});
}

Using async/await with for loop and mongodb

I want to send an order-confirmation email(with nodemailer) to customers when they finished the order.
In this confirmation email I want to insert some data from the buyed products.
I've used many async/await combinations but none of them succeeded.
The code I got for now:
async function getProductData(products) {
let productsArray = products;
try {
let productsHtml = "";
for await (const product of productsArray) { // loop through the products
db.collection("collectionname").findOne(query, (err, data) => { // get data for every product
if (err) console.log(err)
else {
let productHtml = `<p>productname: ${data.productname}</p>
<img src="${data.imageSrc}">`;
productsHtml += productHtml; // add the product html string to productsHtml
}
})
}
} catch (error) {
console.log(error);
}
return productsHtml; //return the productsHtml string. But it returns "", because it doesn't await the mongodb function
}
async function sendConfirmationMail() {
let productsHtml = await getProductData(products); //"products" is an array
//nodemailer code
let transporter = nodemailer.createTransport({
// configuration
});
let email = await transporter.sendMail({
//email information
html: `<p>Thank you for your order!</p>
${productsHtml}`;, // insert the html string for all products into the mail
});
}
The problem is that the return statement in getProductData() fires before the for loop finishes. And then returns undefined.
Does anyone have tips on how I can implement async/await the correct way here?
You are not awaiting your database call. You are passing it a callback, so it does not return a Promise :
.findOne(query, (err, data) => {
This has a callback. You are passing it a function as second argument, that takes (err,data) as its own arguments. You need to remove this callback and use the async/await syntax with try/catch. Something like
const data = await db.collection("collectionname").findOne(query);
On the other hand you don't need to for await (const product of productsArray). You are iterating an array of static values, not an array of Promises. Remove this await.

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

Categories

Resources