i am updating 2 documents using mongoose.save(), but i think the way I am doing is is not safe, as far as i know i need to use async to make sure all documents are being executed
// array containing the 2 documents from db
let schedules
let newItem = {
isActive: room.isActive,
name: room.roomname
};
// adding new items to nested array
schedules[0].rooms.push(newItem);
schedules[1].rooms.push(newItem);
// saving / updating documents
var total = schedules.length,
result = [];
function saveAll() {
var doc = schedules.pop();
doc.save(function(err, saved) {
if (err) throw err; //handle error
result.push(saved);
if (--total) saveAll();
else {
// all saved here
res.json(result);
}
});
}
saveAll();
any explanation how to do it correctly
We can use promise.all for this but we need to change your save function to promise based function
...
var total = schedules.length,
result = [];
function saveAll() {
const promises = schedules.map(schedule => save(schedule));
return Promise.all(promises)
.then(responses => {
// all saved processes are finished
res.json(responses);
})
}
// convert callback `save` function to promise based
function save(doc) {
return new Promise((resolve, reject) => {
doc.save((err, saved) => {
if (err) {
reject(err);
}
resolve(saved);
});
});
}
If you can use async/await we can make saveAll function cleaner
async function saveAll() {
const promises = schedules.map(schedule => save(schedule));
const responses = await Promise.all(promises);
res.json(responses);
}
Hope it helps
Use Promises :
doc1.save().exec().then(
data => {
doc1.save().exec().then(
data2 => console.log(data2)
).catch(err2 => console.log(err2))
}
).catch(err1 => console.log(err1))
Related
I've been making dynamic dropdown box that each option has the table's name of BigQuery and I want to use return value (list) that is made inside .then method in function listTables(). However it seems not to work well . I'm new to Js so could you give any tips ?? Thank you so much.
function listTables() {
const {BigQuery} = require('#google-cloud/bigquery');
const bigquery = new BigQuery({
projectId: 'test_project',
});
const list = [];
bigquery
.dataset("test_table")
.getTables()
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);←I want to use this list outside a function
})
.catch(err => {
console.error('ERROR:', err);
});
}
listTables();
// select tag
let slt = document.getElementById("slt");
addTables(slt);
// return data
function getList() {
return new Promise(function (onFulliflled, onRejected) {
onFulliflled(list);
});
}
function addTables(slt) {
getList()
.then((list) => {
for (item of list) {
// optionを作成
let option = document.createElement("option");
option.text = item;
option.value = item;
// optionの追加
slt.appendChild(option);
}
})
.catch((err) => {
console.error("error", err);
});
}
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);
})
RESULT
[ 'test', 'test1', 'test2', 'test3' ]
You can do this in multiple ways.
Using calllback
function listTables(callback) {
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
callback(list);
})
//...
}
listTables(function(list){
});
Using promise or async/await
function listTables() {
return new Promise(function(resolve, reject){
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
resolve(list);
})
//...
});
}
// Promise
listTables().then(function(list){
});
//Async/await
var list = await listTables();
For the await to work you also need to run in within an async function. For example wrap it in async iife.
(async function(){
var list = await listTables();
})();
I don't use await myself so this is just from top of my head and might need some changes.
I am implementing a Cloud Function where I am executing several queries.
let rating_subcollection = await admin.firestore().collection("restaurants_collection").doc(vendor_id).collection('ratings').where('uID', '==', userId)
.get()
.then(async function (data) {
if (data.empty) {
let restaurants_collection = admin.firestore().collection("restaurants_collection").doc(vendor_id);
await admin.firestore().runTransaction(async (transaction) => {
const restDoc = await transaction.get(restaurants_collection);
// Compute new number of ratings
const newNumRatings = restDoc.data().noRat + 1;
// Compute new average rating
const oldRatingTotal = restDoc.data().rat * restDoc.data().noRat;
const newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;
// Update restaurant info
transaction.update(restaurants_collection, {
rat: newAvgRating,
noRat: newNumRatings
});
})
}
}).catch(error => {
return "Couldnt update the rating: " + error;
})
So, as you can see I am only executing the transaction IF data is empty and I have set async in the then() callback. Is this the right way to do?!
I found this example, where is explained on detail how get a collection using an async function, for example if you want to get a collection:
function getValues(collectionName, docName) {
return db.collection(collectionName).doc(docName).get().then(function (doc) {
if (doc.exists) return doc.data().text;
return Promise.reject("No such document");
}};
}
Or using await it inside an async function in a try/catch block, if you like that better:
async function doSomething() {
try {
let text = await getValues('configuration','helpMessage');
console.log(text);
} catch {
console.log("ERROR:" err);
}
}
I don't know about the correct title for my problem. I just need a value going out of a function, just like return but I think it's not same.
i have code snippet from controller in adonisjs framework:
var nmeadata = "";
jsondata.forEach(element => {
var x = element.nmea
var buff = new Buffer(x, 'base64')
zlib.unzip(buff, (err, res) => {
if(err)
{
//
}
else
{
nmeadata += "greed island"
nmeadata += res.toString()
}
})
});
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
})
I need the result of unzipped string data that inserted to nmeadata from zlib function then send it to view. But, for this time, even I cannot displaying a simple output like greed island to my view.
thank you.
UPDATE
Still not working after using promises:
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
//
} else {
nmeadata += "test add text"
// nmeadata += res.toString()
}
//im also try using resolve() and resolve("any text")
resolve(nmeadata);
})
}
)
)
});
await Promise.all(promises);
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
UPDATE AUGUST 22 2019
i'm already tried solution from maksbd19 but still not working
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve("greed island")
})
}
)
)
});
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
}
I'd suggest two things-
modify zlib.unzip callback function to resolve properly;
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve(res.toString())
}
retrieve the final data from the result of Promise.all
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
In this approach every promise will resolve and finally you will get the expected result in array.
I have a problem with async function and callbacks in Node js. I need to get my friends' all posts and display it. But if i do that without setTimeout(), it returns only some part of data. How can i solve this problem without putting setTimeout? Of course it's absurd to wait for 5-6 or 10 seconds to get all data. I also tried with Promises, but again response is incomplete. Please someone can help me?
//Sending request with axios to Controller
axios.post(packages.proxy+'users/getFriendsPosts',{id: user_id},config)
.then(res => {
// Code for displaying result
})
//User Controller
router.post("/getFriendsPosts", getFriendsPosts);
//Send request body to userService.js
function getFriendsPosts(req, res, next) {
userService.getFriendsPosts(req.body, function(posts, user){
res.json({posts,user});
})
.catch(err => next(err));
}
//userService.js
module.exports = {
getFriendsPosts,
};
async function getFriendsPosts(user,callback){
var arr = [];
var array = [];
MongoClient.connect(url, async function(errr, db) {
if (errr) throw errr;
var dbo = db.db("drone-x");
//Find user
dbo.collection("users").find({_id: ObjectId(user.id)}).toArray(async function(err, result) {
if (err) throw err;
result.forEach(async function(element, index) {
if(element.friends.length != 0){
element.friends.forEach(async function(elem) {
//Find user's friends
dbo.collection("users").find({_id: ObjectId(elem.id)}).toArray(async function(error, res) {
if (error) throw error;
//push user's friends to arr
arr.push(res);
res.forEach(async function(elements) {
//Find user's friends posts
dbo.collection("posts").find({userId: elements._id.toString()}).toArray(async function(errors, results) {
if (errors) throw errors;
//push user's friends posts to array
array.push(results);
//callback results through setTimeout
setTimeout(async function(){ await callback(array, arr); db.close(); }, 2000);
});
});
});
});
}
else
{
await callback("0");
}
});
});
});
}
If i don't use setTimeout function, it just returns 2-3 data, but with setTimeout, it returns all data. And if data will be raise, then i need to increase the setTimeout time. But of course it's not good idea. Someone can help me?
You should use try catch in this code
getFriendsPosts = async (user,callback) => {
const arr = [];
const array = [];
const db = await MongoClient.connect(url);
const dbo = db.db("drone-x");
const results = await dbo.collection("users").find({_id: ObjectId(user.id)})
const resultPromise = _.map(results, async element => {
const friends = _.get(element, 'friends', [])
if(friends.length != 0) {
const friendPromise = _.map(friends, async friend => {
const ress = await dbo.collection("users").find({_id: ObjectId(friend.id)})
arr.push(ress);
const resPromise = _.map(ress, async res => {
const posts = await dbo.collection("posts").find({userId: res._id.toString()})
const postPromise = _.map(posts, post => {
array.push(post);
})
await Promise.all(postPromise)
})
await Promise.all(resPromise)
})
await Promise.all(friendPromise)
}
})
await Promise.all(resultPromise)
return { arr , array }
}
I am not recommand this way it take too much time. You should use mongoose and use aggregation for long Query.
I want to send data back via functions.https.onCall, the code works and returns data to the console but i don't know why I can't send the data back to the device(in return number one, I understand can't why return number 2 is not working because the asynchronous task, so there is a way to to it after its finish?).
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var posts= new Array();
var finalposts = new Array();
function post (uid,text,user_name,vid,sendtime,vote,tag,pic,lan){
this.uid=uid;
this.text=text;
this.user_name=user_name;
this.vid=vid;
this.sendtime=sendtime;
this.vote=vote;
this.tag=tag;
this.pic=pic;
this.lan=lan;
}
exports.getWarm = functions.https.onCall((data, context) => {
finalposts = [];
posts = [];
db.collection('local uploads/heb/posts').where('vote', '<', 200).where('vote', '>', 100).get()
.then((snapshot) => {
snapshot.forEach((doc) => {
posts.push( new post(doc.data()["uid"],doc.data()["text"],doc.data()["user_name"],doc.data()["vid"],
doc.data()["sendtime"],doc.data()["vote"],doc.data()["tag"],doc.data()["pic"],doc.data()["lan"]));
});
posts.sort(function(a,b) {return (a.sendtime<b.sendtime)? 1:((a.sendtime>b.sendtime)? -1:0);});
for (var i =0; i<data.refresh_num*2; i++) {
finalposts.push(posts[i]);
}
console.log('hey', '=>', finalposts);
// working: the posts are shown in the console
// (1.) return { posts: finalposts };
// returns null
})
.catch((err) => {
console.log('Error getting documents', err);
});
// (2)return { posts: finalposts };
// returns {posts=[]}
return { posts: 'data' };
// returns {posts=data}
});
You need to return a Promise from your Firebase function in order to return data. Try adding return before your db.collection and see if that works.
From the docs: https://firebase.google.com/docs/functions/callable
To return data after an asynchronous operation, return a promise.
EDIT: I am not very familiar with Firebase Firestore, but any calls that generate a promise, add a return statement before it.
Also, from my experience, you will want to move your return { posts: 'data' }; to execute after your last promise by using then.
well it was simpler then I thought, I just needed to return it.
exports.getWarm = functions.https.onCall((data, context) => {
finalposts = [];
posts = [];
return db.collection('local uploads/heb/posts').where('vote', '<', 200).where('vote', '>', 100).get()
.then((snapshot) => {
snapshot.forEach((doc) => {
posts.push( new post(doc.data()["uid"],doc.data()["text"],doc.data()["user_name"],doc.data()["vid"],
doc.data()["sendtime"],doc.data()["vote"],doc.data()["tag"],doc.data()["pic"],doc.data()["lan"]));
});
posts.sort(function(a,b) {return (a.sendtime<b.sendtime)? 1:((a.sendtime>b.sendtime)? -1:0);});
for (var i =0; i<data.refresh_num*2; i++) {
finalposts.push(posts[i]);
}
console.log('hey', '=>', finalposts);
return { posts: finalposts };
})
.catch((err) => {
console.log('Error getting documents', err);
});
});