How can I make this code works with async - javascript

router.post("/cart/paycash/add-order",(req,res) => {
req.checkBody("emri","Ju lutem vendosni emrin").notEmpty();
req.checkBody("mbiemri","Ju lutem vendosni mbiemrin").notEmpty();
req.checkBody("numritelefonit","Ju lutem vendosni numrin e telefonit").notEmpty();
req.checkBody("qyteti","Ju lutem vendosni qytetin").notEmpty();
var emri = req.body.emri;
var mbiemri = req.body.mbiemri;
var telefoni = req.body.numritelefonit;
var email = req.body.email;
var qyteti = req.body.qyteti;
var adresa = req.body.adresa;
var Cart = req.session.cart;
var errors = req.validationErrors();
if(errors) {
res.redirect("/cart/checkout", {
errors:errors
});
}
else {
Orders.find({}, function(err,orders) {
if(err) {
console.log(err);
}
else {
var order=new Orders({
emri:emri,
mbiemri:mbiemri,
telefoni:telefoni,
email:email,
qyteti:qyteti,
adresa:adresa,
});
console.log(Cart.length);
Cart.forEach(function(product) {
var cart = Cart.length;
var productTitle = product.title;
console.log(productTitle);
for (var i = 0; i < 1; i++) {
Products.findOne({title:product.title}, function(err,foundproduct) {
console.log(foundproduct.title)
order.products.push(foundproduct);
order.save();;
});
}
});
}
});
delete req.session.cart;
delete req.session.promocode;
res.redirect("/dyqani");
}
});
I want to make this code works, but for this I need async. I have tried some methods but I couldn't made it work. Can anyone help me? I want to be able to add in my order database all the products that are in the cart, but because mongoose is async and JavaScript is not, some of the queries get loaded before and the results in my database are not the ones that are in my cart.

It seems like you're trying to asynchronously request for every product that's in your cart. You don't need the nested for loop in your forEach function, because that loop doesn't seem to do anything.
You need to first map all the async requests in an array and run Promise.all to request them all asynchronously
let products = Cart.map((product) => {
return Products.findOne({title:product.title},(err,foundproduct) => {
//your save product to order logic
});
})
Promise.all(products).then((complete) => {
console.log('product added')
})
Here's a detailed explanation on how promises work in javascript Javascript Promises
Your application design pattern is also uncommon, but that's outside the scope of this question.

Related

How to make react stop duplicating elements on click

The problem is that every time I click on an element with a state things appear twice. For example if i click on a button and the result of clicking would be to output something in the console, it would output 2 times. However in this case, whenever I click a function is executed twice.
The code:
const getfiles = async () => {
let a = await documentSpecifics;
for(let i = 0; i < a.length; i++) {
var wrt = document.querySelectorAll("#writeto");
var fd = document.querySelector('.filtered-docs');
var newResultEl = document.createElement('div');
var writeToEl = document.createElement('p');
newResultEl.classList.add("result");
writeToEl.id = "writeto";
newResultEl.appendChild(writeToEl);
fd.appendChild(newResultEl);
listOfNodes.push(writeToEl);
listOfContainers.push(newResultEl);
wrt[i].textContent = a[i].data.documentName;
}
}
The code here is supposed to create a new div element with a paragraph tag and getting data from firebase firestore, will write to the p tag the data. Now if there are for example 9 documents in firestore and i click a button then 9 more divs will be replicated. Now in total there are 18 divs and only 9 containing actual data while the rest are just blank. It continues to create 9 more divs every click.
I'm also aware of React.Strictmode doing this for some debugging but I made sure to take it out and still got the same results.
Firebase code:
//put data in firebase
createFileToDb = () => {
var docName = document.getElementById("title-custom").value; //get values
var specifiedWidth = document.getElementById("doc-width").value;
var specifiedHeight = document.getElementById("doc-height").value;
var colorType = document.getElementById("select-color").value;
parseInt(specifiedWidth); //transform strings to integers
parseInt(specifiedHeight);
firebase.firestore().collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.add({
documentName: docName,
width: Number(specifiedWidth), //firebase-firestore method for converting the type of value in the firestore databse
height: Number(specifiedHeight),
docColorType: colorType,
creation: firebase.firestore.FieldValue.serverTimestamp() // it is possible that this is necessary in order to use "orderBy" when getting data
}).then(() => {
console.log("file in database");
}).catch(() => {
console.log("failed");
})
}
//get data
GetData = () => {
return firebase.firestore()
.collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.orderBy("creation", "asc")
.get()
.then((doc) => {
let custom = doc.docs.map((document) => {
var data = document.data();
var id = document.id;
return { id, data }
})
return custom;
}).catch((err) => {console.error(err)});
}
waitForData = async () => {
let result = await this.GetData();
return result;
}
//in render
let documentSpecifics = this.waitForData().then((response) => response)
.then((u) => {
if(u.length > 0) {
for(let i = 0; i < u.length; i++) {
try {
//
} catch(error) {
console.log(error);
}
}
}
return u;
});
Edit: firebase auth is functioning fine so i dont think it has anything to do with the problem
Edit: This is all in a class component
Edit: Clicking a button calls the function createFileToDb
I think that i found the answer to my problem.
Basically, since this is a class component I took things out of the render and put some console.log statements to see what was happening. what i noticed is that it logs twice in render but not outside of it. So i took the functions out.
Here is the code that seems to fix my issue:
contain = () => {
const documentSpecifics = this.waitForData().then((response) => {
var wrt = document.getElementsByClassName('writeto');
for(let i = 0; i < response.length; i++) {
this.setNewFile();
wrt[i].textContent = response[i].data.documentName;
}
return response;
})
this.setState({
docs: documentSpecifics,
docDisplayType: !this.state.docDisplayType
})
}
As for creating elements i put them in a function so i coud reuse it:
setNewFile = () => {
const wrt = document.querySelector(".writeto");
const fd = document.querySelector("#filtered-docs");
var newResultEl = document.createElement('div');
newResultEl.classList.add("result");
var wrtEl = document.createElement('p');
wrtEl.classList.add("writeto");
fd.appendChild(newResultEl);
newResultEl.appendChild(wrtEl);
}
The firebase and firestore code remains the same.
the functions are called through elements in the return using onClick.

Sequelize transaction stops executing during loop

I am trying to do a transaction that loops through multiple items and inserts them into the database. If i just have 1 item in each array, the code executes fine, it inserts the deployment, the crew and the equipment. However if I have 2 or more items in either equipment or crew, the application just freezes, no error or anything.
The console output looks like this:
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): START TRANSACTION;
----------------------CREATE DEPLOYMENT---------------
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): INSERT INTO `deployments` (`id
`,`dateDeployed`,`dateReturned`,`city`,`province`,`country`,`fireName`,`fireNumb
er`,`fireCenter`,`unitNumber`,`comments`,`finalSold`,`status`,`createdAt`,`updat
edAt`,`contractId`,`deploymentTypeId`,`productId`) VALUES (DEFAULT,'2018-03-01',
'Invalid date','1','BC','CAN','1','1','1','1','test','','active','2018-03-08 22:
36:44','2018-03-08 22:36:44','5','1','1');
----------------------CREATE EQUIPEMENT---------------
----------------------CREATE EQUIPEMENT---------------
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:31','2018-03-08 22:36:4
4',17,1);
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:39','2018-03-08 22:36:4
4',17,2);
My code is like this:
app.post('/deployment', function(req,res,next){
var deployment = req.body;
var crew = req.body.deploymentCrew;
var equipment = req.body.deploymentEquipment;
var deploymentId = "";
//insert new deployment - start transaction, add deployment, get ID, loop through crew, loop through equipment
models.sequelize.transaction(t =>
{
var equipPromises = [];
console.log('----------------------CREATE DEPLOYMENT---------------');
return models.deployment.create(req.body, {transaction: t})
.then(function(newDeployment) {
deploymentId = newDeployment.dataValues.id;
for (var i = 0; i < equipment.length; i++) {
console.log('----------------------CREATE EQUIPEMENT---------------');
var equip = equipment[i];
equip.deploymentId = deploymentId;
equip.equipmentId = equipment[i].id;
var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
}
return Promise.all(equipPromises);
})
.then(function() {
console.log('----------------------CREATE STAFF---------------');
var crewPromises = [];
for (var i = 0; i < crew.length; i++) {
var staff = crew[i];
staff.deploymentId = deploymentId;
staff.staffId = crew[i].staff.id;
var crewPromise = models.deploymentCrew.create(staff, crewPromises.push(crewPromise));
}
return Promise.all(crewPromises);
});
}).then(result => {
console.log('deployment added');
res.send(result);
}).catch(err => {
console.log('deployment creation failed');
res.status(401).send({'message':err, 'redirect': '/'});
});
});
Any thought's or ideas why this might be happening would be appreciated.
Thanks
It was actually quite simple, I wasn't adding the transaction into the looped create statements. So now it is like:
//this is the old statement
//var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
//this is the new correct way to do it
equipPromises.push(models.deploymentEquipment.create(equip, {transaction:t}));

Remove duplicate entries in parse-server

Parse-server doesn't support groupBy for queries. So instead of adapting code to work with the duplicate entries i've decided to create a Job to clean the data.
I've created a cloud function using underscore but the results are not good. It's deleting non-duplicate entries too.
I want to remove a entry if another entry exists with the same post_id and user_id
Parse.Cloud.job("removeDuplicateItems", function(request, status) {
var _ = require("underscore");
var hashTable = {};
function hashKeyForTestItem(testItem) {
var fields = ["user_id", "post_id"];
var hashKey = "";
_.each(fields, function (field) {
hashKey += testItem.get(field) + "/" ;
});
return hashKey;
}
var testItemsQuery = new Parse.Query("Post_shares");
testItemsQuery.each(function (testItem) {
var key = hashKeyForTestItem(testItem);
if (key in hashTable) { // this item was seen before, so destroy this
return testItem.destroy();
} else { // it is not in the hashTable, so keep it
hashTable[key] = 1;
}
}).then(function() {
status.success("removal completed successfully.");
}, function(error) {
status.error("Uh oh, something went wrong.");
});
});
Is there a better way of doing this?

Parse.com Javascript Asyn Call within Loop

I have a list of company and would like to calculate a total amount of invoices issued to each company. The following is the code that I wrote. (Actual logic is more complicated within the loop but took them out here)
Basically I want to alert the message once the business logic within the loop is complete (Again, it will do something more complex here). I got a feeling that I can resolve this issue by using Promises but am not quite sure how to use it. I didn't quite follow Parse.com's document. I have been stuck with this for a few hours. Please help!
function calculate(companies) {
companies.forEach(function(company) {
var total = 0;
var invoice = Parse.Object.extend('Invoice');
var query = new Parse.Query(invoice);
query.equalTo('invoiceCompany', company);
query.find().then(function(invoices) {
invoices.forEach(function(invoice) {
total += parseFloat(invoice.get('amount'));
});
});
});
alert("Calculated Finished");
}
You can use promises in paralell:
https://parse.com/docs/js/guide#promises-promises-in-parallel
It would be something like this:
function calculate(companies) {
var promises = [];
companies.forEach(function(company) {
var total = 0;
var invoice = Parse.Object.extend('Invoice');
var query = new Parse.Query(invoice);
query.equalTo('invoiceCompany', company);
var queryPromise = query.find().then(function(invoices) {
invoices.forEach(function(invoice) {
total += parseFloat(invoice.get('amount'));
});
});
promises.push(queryPromise);
});
return Parse.Promise.when(promises);
}
calculate(companies).then(function() {
alert("Calculated Finished");
});

closing mongodb connection and get correct results with multiple parallel asynchronous query

I am new to Mongo and Node. I am currently using Mongoskin and Bluebird to handle the db connection and queries (as suggested here: https://stackoverflow.com/a/23687958/2701348 ).
I have three collections: Users, Binders and Cards.
The Binders collection contains the information about Cards for each User.
Each document in Binders has as properties:
User Id <--- that refers to the User owning the Card
Card Code <--- that refers to a Card
Count <--- that refers to the number of cards owned by the User
I prefer to have a separate Cards collection so that when a Card changes, it changes for all the Users Binders.
Now I am willing to retrieve an array for a given user such as:
[{card: {card document}, count: 4}, ...]
I have the following problems:
the db connection should be closed after all the async db callbacks are called
the cards array should be returned after the last db.collection('cards').find gives back the results
I know my following code is wrong but can be a starting point for a discussion:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var promiseCards;
//console.log("------ binderCards: ", binderCards);
var cards = [];
for (var i = 0; i < binderCards.length; i++) {
var binderCard = binderCards[i];
promiseCards = db.collection('cards').find({Code: binderCard.Code}).toArrayAsync();
promiseCards.then(function(cardsDB){
if(cardsDB){
//console.log("Cards found: ",binderCard.Code, cardsDB);
for (var i = 0; i < cardsDB.length; i++) {
cardsDB[i].count = binderCard.count;
};
cards.concat(cardsDB);
}
});
}
promiseCards.then(function(){
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
I am struggling trying to figure out how to handle the promisfied asynchronous call correctly in order to send back the whole array and close the db connection.
I think I should try to build a promise before the for loop and use it to chain the query on Cards promises and lastly chain the db.close() and res.json(cards) statements.
[EDIT] Maybe the easiest solution is to simply use the $in filter inside a single db.collection('cards').find({Code: {$in: [bindersCodeArray] }}).toArrayAsync(); and avoid that for loop:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var binderCodes = binderCards.map(function(element){
return element.Code;
});
var promiseCards = db.collection('cards').find({Code: {$in: binderCodes} }).toArrayAsync();
promiseCards.then(function(cards){
var bindersDictionary = {};
for (var i = 0; i < binderCards.length; i++) {
bindersDictionary[binderCards[i].Code] = binderCards[i].count;
};
for (var i = 0; i < cards.length; i++) {
cards[i].count = bindersDictionary[cards[i].Code];
};
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
Still I am curious if there is an elegant way to solve this riddle using promises.
I would expect that using $in and array may have constraints on the number of binders you can pass and affect query performance. You can also try doing this with async#map. e.g.:
...
function(binders) {
async.map(binders, cardsForBinders, function(err, bindersWithCards) {
// TODO: close connection here.
}
function cardsForBinders(binder, callback) {
// 1. find cards for binder.
// 2. prepare transformed response: binderWithCards.
callback(null, binderWithCards);
}
}
...

Categories

Resources