Nodejs asynchronous function print data before execution fished - javascript

I am using following NodeJS module for getting onvif device in the network https://github.com/futomi/node-onvif, and which works fine.
But using below code the list of device found is always empty. I checked the getDeviceData function and which is getting the device and print data.
But using below code the line console.log(JSON.stringify(list)); execute before all scan process completed. How can I fix it.
async function getDeviceData(info){
var device = new onvif.OnvifDevice({
xaddr: info.xaddrs[0],
user : 'admin',
pass : '123456'
});
await device.init();
var dev_info = device.getInformation();
var rtsp_url = device.getUdpStreamUrl();
var data = {"Manufacturer":dev_info.Manufacturer,"Model":dev_info.Model};
console.log(data); // this print last
return data;
}
const onvif = require('node-onvif');
onvif.startProbe().then((device_info_list) => {
var list = [];
device_info_list.forEach((info) => {
var data = getDeviceData(info)
list.push(data);
});
console.log(JSON.stringify(list)); // this print first
}).catch((error) => {
console.error(error);
});

Here is what you want:
You need to use await to wait the promise to resolve with the data.
onvif.startProbe().then(async (device_info_list) => {
var list = [];
await Promise.all(device_info_list.map(async(info) => {
var data = await getDeviceData(info)
list.push(data);
}));
console.log(JSON.stringify(list)); // this print first
}).catch((error) => {
console.error(error);
});

The data const in the first function returns a promise so would have yo await that. So this should work:
async function getDeviceData(info) {
const device = new onvif.OnvifDevice({
xaddr: info.xaddrs[0],
user: 'admin',
pass: '123456',
});
await device.init();
const dev_info = device.getInformation();
const rtsp_url = device.getUdpStreamUrl();
const data = { Manufacturer: dev_info.Manufacturer, Model: dev_info.Model };
await Promise.resolve(data);
console.log(data); // this print last
return data;
}
onvif
.startProbe()
.then(device_info_list => {
const list = [];
device_info_list.forEach(async info => {
const data = await getDeviceData(info);
await Promise.resolve(list.push(data));
});
console.log(JSON.stringify(list)); // this print first
})
.catch(error => {
console.error(error);
});

Related

Close MONGODB connection after data insertion

I want to populate my database with some random data. I have used Faker.js for generating that data. I'm using MongoDB on my localhost and all the data is properly following all the validation rules from the schema. I'm having problem with the closing connection of my connection after insertion of data. I want to close the connection soon after the data is populated. I'm using async function to be aware of all the things but something is not going right.
Here is my code seeds.js which is the script im using to populate database
const path = require("path");
require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
var mongoose = require("mongoose");
mongoose.connect(process.env.MONGODB_URI);
require("../models/User");
require("../models/Item");
require("../models/Comment");
var Item = mongoose.model("Item");
var Comment = mongoose.model("Comment");
var User = mongoose.model("User");
const ItemData = require("../data/item.json");
const CommentData = require("../data/comment.json");
const UserData = require("../data/user.json");
async function InsertData() {
ItemData.forEach(async (item) => {
item.seller = item.seller.$oid;
const oldItem = await Item.find({ title: item.title });
if (!oldItem.length) {
var newItem = new Item(item);
await newItem.save();
} else {
console.log(item.slug);
}
});
UserData.forEach(async (user) => {
const oldUser = await User.find({ username: user.username });
if (!oldUser.length) {
var user = new User(user);
await user.save();
} else {
console.log(user.username);
}
});
CommentData.forEach(async (comment) => {
comment.item = comment.item.$oid;
comment.seller = comment.seller.$oid;
var newComment = new Comment(comment);
const oldComment = await Comment.find({ _id: newComment.id });
if (!oldComment.length) {
await newComment.save();
} else {
console.log(comment.body);
}
});
}
async function cleanup() {
await Item.deleteMany({}, () => console.log("Data Cleared Item"));
await Comment.deleteMany({}, () => console.log("Data Cleared Comment"));
await User.deleteMany({}, () => console.log("Data Cleared User"));
}
async function main() {
InsertData().then(async () => {
console.debug('Data Inserted. Closing connection.');
await mongoose.connection.close();
});
}
main();
Here is the stack trace of the error
/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/connection/pool.js:841
cb(new MongoError('pool destroyed'));
^
MongoError: pool destroyed
at Pool.write (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/connection/pool.js:841:8)
at _command (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/command.js:120:10)
at command (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/command.js:28:5)
at Object.query (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/query.js:66:3)
at Server.query (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/topologies/server.js:644:16)
at FindOperation.execute (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/find.js:38:12)
at /Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:144:17
at Server.selectServer (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/topologies/server.js:832:3)
at Server.selectServer (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/topologies/topology_base.js:342:32)
at executeWithServerSelection (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:131:12)
at /Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:70:9
at maybePromise (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/utils.js:685:3)
at executeOperation (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:34:10)
at Cursor._initializeCursor (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/cursor.js:534:7)
at Cursor._initializeCursor (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/cursor.js:186:11)
at nextFunction (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/cursor.js:737:10)
error Command failed with exit code 1.

Storing MongoDB document inside a Node.js array and return it

I try to get specific documents from MongoDB with Node.js and insert them into array.
const getStockComments = async (req) => {
const stockname = req.params.stockName;
var comments = [];
var data = [];
const stock = await stockModel.findOne({ name: stockname });
comments = stock.comments;
comments.forEach(async (commentId) => {
const comm = await commentModel.findOne({ _id: commentId });
data.push(comm);
console.log(data); // This returns the data in loops, because its inside a loop.
});
console.log(data); // This not returns the data and i don't know why.
return data;
};
The first console.log(data) returns the same data a lot of times because its inside a loop.
But the second console.log(data) dosen't returns the data at all.
What I'm doing wrong?
Instead of using loop , you can use $in operator to simplify things .
const getStockComments = async (req) => {
const stockname = req.params.stockName;
var comments = [];
var data = [];
const stock = await stockModel.findOne({ name: stockname });
comments = stock.comments;
commentModel.find({ _id: { $in: comments } }, (err, comments) => {
data = comments;
});
console.log(data);
return data;
};

How to make promise to wait for all objects to be complete and then push to array?

The getURL() function creates an array of scraped URLs from the original URL. getSubURL() then loops through that array and scrapes all of those pages' URLs. Currently, this code outputs just fine to the console, but I don't know how to wait for my data to resolve so I can push all gathered data to a single array. Currently, when I try and return sites and then push to array, it only pushes the last value. I believe it's a promise.all(map) situation, but I don't know how to write one correctly without getting an error. Ideally, my completed scrape could be called in another function. Please take a look if you can
const cheerio = require('cheerio');
const axios = require('axios');
let URL = 'https://toscrape.com';
const getURLS = async () => {
try {
const res = await axios.get(URL);
const data = res.data;
const $ = cheerio.load(data);
const urlQueue = [];
$("a[href^='http']").each((i, elem) => {
const link = $(elem).attr('href');
if (urlQueue.indexOf(link) === -1) {
urlQueue.push(link);
}
});
return urlQueue;
} catch (err) {
console.log(`Error fetching and parsing data: `, err);
}
};
const getSubURLs = async () => {
let urls = await getURLS();
try {
//loop through each url in array
for (const url of urls) {
//fetch all html from the current url
const res = await axios.get(url);
const data = res.data;
const $ = cheerio.load(data);
//create object and push that url into that object
let sites = {};
sites.url = url;
let links = [];
//scrape all links and save in links array
$("a[href^='/']").each((i, elem) => {
const link = $(elem).attr('href');
if (links.indexOf(link) === -1) {
links.push(link);
}
//save scraped data in object
sites.links = links;
});
// returns list of {url:'url', links:[link1,link2,link3]}
console.log(sites);
}
} catch (err) {
console.log(`Error fetching and parsing data: `, err);
}
};
Don't think this is a Promise related issue at heart.
You'll need to collect your sites into an array that is initialized outside the loop. Then when getSubURLs() resolves, it will resolve to your array:
const getSubURLs = async() => {
let urls = await getURLS();
let siteList = [];
try {
for (const url of urls) {
// :
// :
// :
siteList.push(sites);
}
} catch (err) {
console.log(`Error fetching and parsing data: `, err);
}
return siteList; // array of objects
};
getSubURLs().then(console.log);

Sending list of images as response using Javascript

I am making an API that gets a list of image names, then it has to download them one by one from S3 bucket and then send them all as a response.
The issue is that my images are being uploaded but it seems that when I put them in a list as base64 and then try to send the list then the list just comes up empty.
const getImagesById = async (req, res) => {
const { id } = req.params;
const imagesSet = new Map();
try {
const documentFromDB = await document.findOne({ id });
documentFromDB.devices.forEach((device) => {
const images = new Set();
device.images.forEach(item => images.add(downloadFromS3(item)))
imagesSet.set(device.name, JSON.stringify(mapToObj(images))) // tried adding just images also but neither works
});
res.status(200).json(JSON.stringify(mapToObj(imagesSet)));
} catch (e) {
console.log(`An error occurred : ${e.message}`);
res.status(500)
.send(e.message);
}
};
function mapToObj(inputMap) {
let obj = {};
inputMap.forEach(function(value, key){
obj[key] = value
});
return obj;
}
And this is how I get images from S3:
const downloadFromS3 = async (imageName) => {
try {
const image = await S3Utils.downloadFile(BUCKET_NAME, imageName);
if (image.stack) {
return null;
}
const imageBase64 = image.Body.toString('base64');
return imageBase64;
} catch (e) {
console.log(`An error occurred while downloading : ${e.message}`);
throw e;
}
};
This is the response I am getting at the moment:
"{\"{ name: 'Martin'}\":\"{\\\"[object Promise]\\\":{}}\"}"
What I am trying to do is get a lits of device names, map them in a Map as key with value as the base64 list of images and then send it all in a response to the UI to show the images with the names.
What am I doing wrong here?
You just need to add await before call the downloadFromS3 function, consequently changing all the above functions.
const getImagesById = async (req, res) => {
const { id } = req.params;
const imagesSet = new Map();
try {
const documentFromDB = await document.findOne({ id });
await Promise.all(documentFromDB.devices.map(async (device) => {
const images = new Set();
await Promise.all(device.images.map(async item => images.add(await downloadFromS3(item))))
imagesSet.set(device.name, JSON.stringify(mapToObj(images))) // tried adding just images also but neither works
}));
res.status(200).json(JSON.stringify(mapToObj(imagesSet)));
} catch (e) {
console.log(`An error occurred : ${e.message}`);
res.status(500)
.send(e.message);
}
};
function mapToObj(inputMap) {
let obj = {};
inputMap.forEach(function(value, key){
obj[key] = value
});
return obj;
}

How to use promise and loop over mongoose collection

I'm making chat inside my website. To store data I use Chat, User, Messages collections.
I want results to be in Array containing:
[{
username (another one, not me)
last update
last message
}]
In Chat model I have only chatid and array of two members, so I need to loop through User collection to get user name using user id from it. I want to save in array all names (in future I would also like to loop through messages to get latest messages for each chatid). Issue is that when I return chatsList it is empty. I think I need somehow to use Promise, but I'm not completely sure how it should work.
Chat.find({ members: userId })
.then(chats => {
let chatsList = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
User.findOne({ _id: guestId })
.then(guest => {
let chatObj = {};
name = guest.name;
chatsList.push(name);
console.log("chatsList", chatsList)
})
.catch(err => console.log("guest err =>", err))
})
return res.json(chatsList)
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
Indeed, Promise.all is what you are looking for:
Chat.find({ members: userId })
.then(chats => {
let userPromises = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
userPromises.push(User.findOne({ _id: guestId }));
});
return Promise.all(userPromises).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
although it would probably be better to do a single call to DB with a list of ids ($in query). Something like this:
Chat.find({ members: userId })
.then(chats => {
let ids = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
ids.push(guestId);
});
return User.find({_id: {$in: ids}}).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
You may want to additionally validate if every id had a corresponding guest.
You are running into concurrency issues. For example, running chats.forEach, and inside forEach running User.findOne().then: The return statement is already executed before the User.findOne() promise has resolved. That's why your list is empty.
You could get more readable and working code by using async/await:
async function getChatList() {
const chats = await Chat.find({members: userId});
const chatsList = [];
for (const chat of chats) {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
const guest = await User.findOne({_id: guestId});
chatsList.push(guest.name);
}
return chatsList;
}
Then the code to actually send the chat list back to the user:
try {
return res.json(await getChatList());
} catch (err) {
// handle errors;
}
You can try this:
Chat.find({ members: userId }).then(chats => {
let guestHashMap = {};
chats.forEach(chat => {
let guestId = chat.members.filter(id => id != userId)[0];
// depending on if your ID is of type ObjectId('asdada')
// change it to guestHashMap[guestId.toString()] = true;
guestHashMap[guestId] = true;
})
return Promise.all(
// it is going to return unique guests
Object.keys(guestHashMap)
.map(guestId => {
// depending on if your ID is of type ObjectId('asdada')
// change it to User.findOne({ _id: guestHashMap[guestId] })
return User.findOne({ _id: guestId })
}))
})
.then(chats => {
console.log(chats.map(chat => chat.name))
res.json(chats.map(chat => chat.name))
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})

Categories

Resources