Nedb async function to be awaited - javascript

async function count() {
let nedb = <<functional nedb database>>
let count = 0
await iterate(nedb, {}, g=>{
count++
})
console.log(count)
iterate(xx, query, callback) {
return new Promise((res, rej)=> {
pkgs.find(query).exec((err, res)=>{
if(err!==null) {
this.err(err)
} else {
res.forEach((pkg)=>{
callback(pkg)
})
}
})
res()
})
I would like to write something, after the iteration is over, is there way to do it in some normal way?

You need to put the res()olve call inside the find callback. Immediately fulfilling the promise to undefined won't help.
But you really shouldn't pass an iteration callback at all. Instead, write
async function count() {
let nedb = …;
let count = 0;
const pkgs = await query(nedb, {});
pkgs.forEach(pkg => count++);
// or maybe better just:
const count = pkgs.length;
console.log(count);
}
function query(pkgs, query) {
return new Promise((resolve, reject)=> {
pkgs.find(query).exec((error, result)=>{
if (error !== null) reject(error);
else resolve(result);
});
});
}

Related

How to wait all async recursive function in Node.js?

I have a list from the request body that I want to parse all links in request async.
When all tasks are finished, I then want to return a response, but my response is always empty because none of the async tasks are finishing.
var index = 0;
let items = [];
exports.feed = async (req, res) => {
(async () => {
await getSiteData(req.body.sites)
})();
if(typeof items !== 'undefined' && items.length > 0){
res.status(200).json(items);
} else {
res.status(404).json({ error: "Something went wrong", success: false });
}
}
async function getSiteData(list){
try{
if(index == list.length-1){
return items;
}
await parser.parseURL(list[index], async function(err, feed){
if(err != null){
console.log(err);
return false;
}
items.push(feed.items)
index++
await getSiteData(list);
});
} catch(err){
console.log(err);
return false;
}
}
Firstly, there's no recursion required. It (almost?) never makes sense to recursively process a flat list (Array)
Your main issue is that parser.parseURL does NOT return a Promise, therefore awaiting it makes no sense, since await only waits for Promises to settle
So, let's fix that by creating a "promisified" parser.parseURL, which is easy with nodes util.promisify
const { promisify } = require('util');
const parseURL = promisify(parser.parseURL);
now you can await parseURL(url)
if you need to parse.parseURL in series, i.e. one at a time
async function getSiteData(list) {
const result = [];
for (const url of list) {
const { items } = await parseURL(url);
result.push(items);
}
return result;
}
to parse.parseURL in parallel
function getSiteData(list) {
return Promise.all(list.map(parseURL));
}
Some people think that this should be
async function getSiteData(list) {
return await Promise.all(list.map(parseURL));
}
I'm not one of those people. Their argument is, getSiteData returns a Promise, so should be marked as such, some malarkey about IDE hints or some such garbage. While the code is equivalent (not identical of course), it really isn't necessary in my opinion.
Note: now getSiteData (in either series or parallel form) returns a Promise that will resolve to an array of results, and, further more, neither version does any error handling. This is deliberate, so the function that calls getSiteData determine what to do with errors.
The other issus is in your feed export
(async () => {
await getSiteData(req.body.sites)
})();
//
// the code down here runs without waiting for getSiteData to complete
this runs that asynchronous IIFE, but the code afterwards does not wait for it to complete
You could
await (async () => {
await getSiteData(req.body.sites)
})();
But then, that's just
await getSiteData(req.body.sites);
Without the IIFE
Since the getSiteData now returns the list (rather than using a "global" or external array to hold the list), the code is now:
exports.feed = async (req, res) => {
try {
const items = await getSiteData(req.body.sites);
res.status(200).json(items);
} catch (err) {
res.status(404).json({error: "Something went wrong", success: false});
}
};
I fixed my issue with using for loop and promise.
exports.feed = async (req,res) => {
var items = [];
for(const elem of req.body.sites){
const newElement = await getSiteData(elem)
items.push(newElement)
}
if(typeof items !== 'undefined' && items.length > 0){
res.status(200).json(items);
}else{
res.status(404).json({ error: "Something went wrong", success: false });
}
}
function getSiteData(url) {
return new Promise((resolve) => {
setTimeout(()=>{
parser.parseURL(url, function(err, feed) {
if(err != null){
return false;
}
resolve(feed.items)
})
});
});
}

Variable undefined after 2nd call

In the 1st function 'get_files()' I can log the file_list variable, it is correct here, however when I log it again in my 2nd function 'get_diffs()' it is undefined..
// Get files
async function get_files() {
await fs.readdir(dirPath, function (err, files) {
(async () => {
if (await err) {
console.log("Error getting directory information.", err)
} else {
var file_list = []; // Reset
await files.forEach(function (file) {
file_list.push(file);
});
console.log('1st Call = ' + file_list); // Correct
return await file_list;
}
})();
});
}
// Get Diffs
async function get_diffs() {
const file_list = await get_files();
console.log('2nd Call = ' + file_list); // Undefined
const dates = await get_dates();
return await files.filter(x => !dates.includes(x));
}
You have misunderstood async/await. Learn the basics here
function get_files() {
return new Promise((resolve, reject) => {
fs.readdir(dirPath, function (err, files) {
if (err) {
reject(err);
} else {
var file_list = []; // Reset
files.forEach(function (file) {
file_list.push(file);
});
console.log('1st Call = ' + file_list); // Correct
resolve(file_list);
}
});
})
}
fs.readdir does not return a promise. Use the promise based function fs.promise.readdir instead.
async function get_diffs() {
const file_list = await fs.promise.readdir(dirPath);
// ...
}
So you don't really need the other function. It had many problems anyway. await makes not much sense when used with an expression that is not a promise. All the places where you have used await in get_files, the expression that follows it does not represent a promise.

How to make async/wait inside of for loop?

I'm using async await inside of for loop as below.
for (let i = 0; i < result.length; i += 1) {
try{
const client = await axios.get(
`${process.env.user}/client/${result[i].id}`
);
} catch(error){
console.log(error)
}
if (client.data.success === true) {
result[i].Name = rider.data.client.Name;
result[i].PhoneNumber = rider.data.client.Number;
}
}
But I want to make this using 'new Promise' and 'promiss.all' to make it asynclously.
But I don'k know how to make this correctly doing error handle well.
Could you recommend some advice for this? Thank you for reading it.
This can be a basic solution, i think
let playList = []
for (let i = 0; i < result.length; i += 1) {
playList.push(axios.get(
`${process.env.user}/client/${result[i].id}`
).then(res => {
if (res.data.success === true) {
result[i].Name = rider.data.client.Name;
result[i].PhoneNumber = rider.data.client.Number;
}
}).catch(ex => console.log(ex)));
}
await Promise.all(playList)
This can also be done by using a foreach loop.
The for/foreach loop can be simplified by using a map function.
Js map function is equivalent of c# select linq function.
The fat arrow in js map function is not bound to return a value unlike c# select inner function which must return a value.
await Promise.all(result.map(async r => {
let client;
try {
client = await axios.get(`${process.env.user}/client/${r.id}`);
} catch (error) {
console.log(error)
}
if (client.data.success === true) {
r.Name = rider.data.client.Name;
r.PhoneNumber = rider.data.client.Number;
}
}));
Try this
var promises = result.map(r => axios.get(`${process.env.user}/client/${r.id}`);
Promise.all(promises).then(function(values) {
console.log('All promises done');
});
The idea is that if you are awaiting something, that is promise, you can await it, or call it to get promise
Example:
function Foo()
{
return new Promise(...); // Promise of int for example
}
you can do
var p = Foo(); //you will get promise
Or
var v = await Foo(); // you will get int value when promise resolved
This is how you do it with async/await + Promise.all:
const myResult = await Promise.all(result.map(({ id }) => {
return axios.get(`${process.env.user}/client/${id}`);
}));
// deal with the result of those requests
const parsed = myResult.map(data => /* your code here */);
Here is an example using Array.map to call your function along with Promise.all. I wrapped the axios request in a function so if one of your request fails, it wont stop every other requests. If you don't mind stopping when you got an issue, look at others answers to your question.
function fakeRequest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
data: {
success: true,
client: {
Name: 'Todd',
Number: 5,
},
},
});
}, 300);
});
}
(async() => {
const result = [{}, {}, {}];
await Promise.all(result.map(async(x, xi) => {
try {
const client = await fakeRequest();
if (client.data.success === true) {
result[xi].Name = client.data.client.Name;
result[xi].PhoneNumber = client.data.client.Number;
}
} catch (err) {
console.log(err)
}
}));
console.log(result);
})();

How to recover a result of query mongodb outside of its function?

I am a new javascript user and I am looking for a rescuer the result (count) of a memory request.
More precisely, maybe you can comment, put my variable in my conv object
right now my result is [promised object]
I tried with async and promised javascript but I do not understand the logic yet
Thank you so much,
Gabriel
var conversations = []
for (var i = 0; i < conversation.length; i++) {
for (var j = 0; j < conversation[i].users.length; j++) {
conv = conversation[i].users[j]
async function asyncCall() {
function countUnread() {
filterUnread = {$and:[
{toID: user.properties.chat.id},
{fromID: conversation[i].users[j].id},
{ read: false }]}
return new Promise(resolve => {
Message.countDocuments(filterUnread, function (err, count) {
resolve(count)
})
})
}
var count = await countUnread();
console.log(count);
console.log(conv)
resolve(!!!!count!!!!) ;
}
asyncCall();
conv.unread = !!!!I_want_my_count_here!!!!
conversations.push(conv);
resolve(conversations)
}
}
Mixing async functions and Promise constructors makes the code hard to track and causes problems like this one. Instead only wrap the most low level parts into Promise constructors, let tuem resolve to something useful, then compose multiple such Promises using async :
const filter = { users: { $elemMatch: { id: user.properties.chat.id } } }
function getConversations(filter) {
return new Promise(function(resolve, reject) {
Conversation.find(filter, function(err, conversations) {
if(err) reject(err) else resolve(conversations);
});
});
}
function countUnread(user) {
const filterUnread = { $and:[
{toID: user.properties.chat.id},
{fromID: user.id},
{ read: false }
]};
return new Promise((resolve, reject) => {
Message.countDocuments(filterUnread, function (err, count) {
if(err) reject(err) else resolve(count);
});
});
}
async function composeConversations() {
const conversations = await getConversations();
const result = [];
for(const conversation of conversations) {
for(const user of conversation.users) {
const count = await countUnread(user);
user.unread = count;
}
result.push(conversation);
}
return result;
}

Using While loop inside async function?

I'm trying to use a while loop inside an async function, and I suspect my resolve(); is messing up the loop, but I'm not sure what to do about it. Here's the code I have:
app.get("/meal-plan", async function(req, res) {
var calorieCount = req.query.calorieCount;
var mealInput = parseInt(req.query.mealInput);
var dietInput = req.query.food;
var goalPerMeal = (calorieCount / mealInput);
var whichDiet = "diet." + dietInput;
var breakfastGoal = goalPerMeal;
var breakfastArr = [];
async function getBreakfastArr() {
return new Promise((resolve, reject) => {
var breakfastQuery = {"breakfast": true, [whichDiet]: true};
while (breakfastGoal >= 150) {
Food.count(breakfastQuery, function(err, count) {
if (err) {
console.log(err);
} else {
var random = Math.floor(Math.random() * count);
Food.findOne(breakfastQuery).skip(random).exec(
function(err, result) {
if (err) {
console.log(err);
} else {
breakfastGoal -= result.nutrition.calories;
breakfastArr.push(result);
resolve();
}
});
}
})
}
});
}
try {
await getBreakfastArr();
console.log(breakfastArr);
res.render("meal-plan.ejs", { meal: mealInput, calories: calorieCount, diet: dietInput, breakfast: breakfast, lunch: lunch, dinner: dinner, totalCalories: totalCalories});
} catch (e) {
res.json(e);
}
});
The goalPerMeal variable takes a user calorie input and divides it by the number of meals they select on my form. I then set that value to a specific breakfast variable called breakfastGoal. The async function finds a random recipe from my database and adds it to my array, breakfastArr. Once it finds the recipe, it subtracts that recipe's calorie count from the breakfastGoal.
I want this function to run until breakfastGoal is reduced to below 150; however, it doesn't seem to work. If I remove the while loop, the function will successfully find the breakfast item, add it to the array, and subtract its calorie count from breakfastGoal. The only thing breaking it is adding the while loop.
Is this something to do with the resolve(); in the async function, or am I missing something important?
Any help is extremely appreciated.
Do you think something like this will work? I have not tested this. Just an idea.
put the while block inside the else block and call recursively.
async function getBreakfastArr() {
let breakfastQuery = { "breakfast": true, [whichDiet]: true };
return this.getRandomRecipe(breakfastQuery).then((response)=>{
return response; //the response will have your breakfast goal
});
}
async function getRandomRecipe(breakfastQuery) {
return Food.count(breakfastQuery, function (err, count) {
if (err) {
console.log(err);
} else {
var random = Math.floor(Math.random() * count);
Food.findOne(breakfastQuery).skip(random).exec(
function (err, result) {
if (err) {
console.log(err);
} else {
breakfastGoal -= result.nutrition.calories;
breakfastArr.push(result);
while (breakfastGoal >= 150) {
this.getRandomRecipe(breakfastQuery);
}
return Promise.resolve(breakfastGoal);
}
});
}
})
}
The problem is that you create only one promise, but have several asynchronous results you need to wait for in the while loop.
The first time resolve is called, that sole promise is resolved, and that will make the code following the await to execute. But at that time your array is not finished at all. Also, any further calls to resolve will not influence that promise any more: a promise can only be resolved once. The other calls are ignored.
The solution is to make a promise in each iteration of the while loop, and await it.
Change this:
return new Promise((resolve, reject) => {
var breakfastQuery = {"breakfast": true, [whichDiet]: true};
while (breakfastGoal >= 150) {
to this:
var breakfastQuery = {"breakfast": true, [whichDiet]: true};
while (breakfastGoal >= 150) {
await new Promise((resolve, reject) => {
The code could be improved more, but it is not relevant to your question. For instance, it would be better to make each promise resolve with the value to be added to the array breakfastArr, so that you would have:
breakfastArr.push(await new Promise((resolve, reject) => {
// ...
resolve(result)
// ...
});
And the async function should return that array, so that it does not have to be defined at the top.
The issue here is that you are mixing callback taking functions with promises. The proper way to do this is to wrap only the callback taking functions in promise constructors. The result is some thing like this:
app.get("/meal-plan", async function(req, res) {
var calorieCount = req.query.calorieCount;
var mealInput = parseInt(req.query.mealInput);
var dietInput = req.query.food;
var goalPerMeal = (calorieCount / mealInput);
var whichDiet = "diet." + dietInput;
var breakfastGoal = goalPerMeal;
function count(query) {
Food.count(query, function(err, result) {
if (err) {
reject(error)
} else {
resolve(result)
}
});
}
function findOne(query, count) {
return new Promise((resolve, reject) => {
Food.findOne(query).skip(count).exec(function(err, result) {
if (err) {
reject(error)
} else {
resolve(result)
}
});
});
}
async function getBreakfastArr() {
let breakfastQuery = {
"breakfast": true,
[whichDiet]: true
};
let breakfastArr = [];
while (breakfastGoal >= 150) {
let count = await count(breakfastQuery);
let random = Math.floor(Math.random() * count);
let result = await findOne(breakfastQuery, random);
breakfastGoal -= result.nutrition.calories;
breakfastArr.push(result);
}
return breakfastArr
}
try {
let breakfastArr = await getBreakfastArr();
console.log(breakfastArr);
res.render("meal-plan.ejs", {
meal: mealInput,
calories: calorieCount,
diet: dietInput,
breakfast: breakfast,
lunch: lunch,
dinner: dinner,
totalCalories: totalCalories
});
} catch (e) {
res.json(e);
}
});
Some of the Promise libraries had a function called promisify that would take a function taking a callback as its last argument which took node style (err, result) arguments and produced a function that when called returned a promise. If that is available the wrappers get a lot smaller. for example the Food.count wrapper becomes let count = promisify(Food.count); orlet count = promisify(Food.count.bind(Food));
You are returning a promise from async function which is not required. You can remove async from the function returning promise.
So I suggest removing async from function getBreakfastArr()

Categories