Promise.all returning undefined in Node JS - node.js

I have a code to fetch directory names from first API. For every directory, need to get the file name from a second API. I am using something like this in my Node JS code -
async function main_function(req, res) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
console.log(tempPromises); // Prints [ Promise { <pending> } ]
Promise.all(tempPromises).then((result_new) => {
console.log(result_new); // This prints "undefined"
res.send({ status: "ok" });
});
});
}
async function getFilename(inp_a) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
return new Promise((resolve) => {
resolve("Temp Name");
});
});
}
What am I missing here?

Your getFilename() doesn't seem to be returning anything i.e it's returning undefined. Try returning response at the end of the function,
async function getFilename(inp_a) {
const response = ...
return response;
}

Thanks to Mat J for the comment. I was able to simplify my code and also learn when no to use chaining.
Also thanks to Shadab's answer which helped me know that async function always returns a promise and it was that default promise being returned and not the actual string. Wasn't aware of that. (I am pretty new to JS)
Here's my final code/logic which works -
async function main_function(req,res){
try{
const response = await fetch(...)
const resp = await response.text();
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
Promise.all(tempPromises).then((result_new) => {
console.log(result_new);
res.send({ status: "ok" });
});
}
catch(err){
console.log(err)
res.send({"status" : "error"})
}
}
async function getFilename(inp_a) {
const response = await fetch(...)
respText = await response.text();
return("Temp Name"); //
}

Related

Unable to map inside an async function

In the below code, I am fetching data from an external api. After parsing the data as json, I wanted to map through it and get a modified version.
For some reason, the console.log(jsonData) inside the map function is not getting executed. Please check the code below for clarity
const getRandomOutfit = async (req, res) => {
const { gender, countryCode } = req.params;
if (req.params.gender === "FEMALE" || req.params.gender === "MALE") {
try {
const response = await fetch(URL);
const jsonData = await response.json();
const outputData = jsonData.map((productItem) => {
console.log(productItem); // doesn't get printed
// some operation
return productItem;
});
await res.json(jsonData);
} catch (error) {
res.status(500).send("Error getting data");
}
} else {
res.status(500).send("Invalid category");
}
};
I'm confused about what I am missing here and making an error.
I rewrote the code to make it clearer to understand. In general, it is best to take the fail first approach. Notice, how the first thing I do is return upon failure.
As to why you code is not printing anything out, try printing jsonData. It might be that this is an empty array.
const getRandomOutfit = async (req, res) => {
const { gender, countryCode } = req.params;
if (gender !== "FEMALE" && gender !== "MALE")
return res.status(500).send("Invalid category");
try {
const response = await fetch(URL);
const jsonData = await response.json();
console.log(jsonData); // what does this return?
const outputData = jsonData.map((productItem) => {
console.log(productItem); // doesn't get printed
// some operation
return productItem;
});
await res.json(jsonData);
} catch (error) {
res.status(500).send("Error getting data");
};

Express router not awaiting the forEach loop

Express router is not awaiting my forEach loop and sends the old unmanipulated object as a response instead of the new manipulated data.
Here I am using Sequalize as my ORM.
router.get('/', async (req,res) => {
try {
let trainings = await db.Training.findAll();
let locations = await db.Location.findAll();
await locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
res.status(200).json({
training:trainings
})
} catch(err) {
console.log(err);
res.status(404).json({
message : err
})
}
})
Basically you are using the await keyword against a synchronous process which is
locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
These lines of code doesn't return any promise or behave like a promise. So one solution can be having a function
function modify(trainings,locations){
return new Promise((resolve,reject)=>{
locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
resolve('done')
})
}
then have it like this
let trainings = await db.Training.findAll();
let locations = await db.Location.findAll();
await modify(trainings,locations)
or you can simply remove the await keyword from your current state of code.

Get value out of function in Node.js

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.

async function in node only runs after response sent

I have the method:
const getTaskOrCategoryRecursive = async (type, id)=>{
const rslt = await type.findOne.call(type, {_id:id},{}, standardOptions, err=>{
if(err){
return({err: err});
}
});
const result = rslt.toObject();
const tasks = result.tasks;
const events = result.events;
result.children = {};
result.children.tasks = tasks.map(async taskId=>{
const taskIdString = taskId.toString();
const rtrn = await getTaskRecursive(taskIdString, err=>{
if(err){
return({err: err});
}
});
return rtrn;
});
result.children.events = events.map(async eventId=>{
const eventIdString = eventId.toString();
await Event.findOne({_id:eventIdString}, standardOptions,err=>{
if(err){
return({err: err});
}
});
});
return result;
}
It's called by two methods:
const getTaskRecursive = (taskId)=>{
return getTaskOrCategoryRecursive(Task, taskId)
}
and
const getCategoryRecursive=(categoryId)=>{
return getTaskOrCategoryRecursive(Category,categoryId);
};
which are called by the function
router.get('/:id', verifyToken, async (req,res)=>{
Branch.getCategoryRecursive(req.params.id)
.then(
(cat)=>{
res.status(200).send(cat);
},
(err)=>{
res.status(500).send(err);
}
)
});
When I try to run the program, first the getCategoryRecursive method runs, then res.status(200).send(cat); then the getTasksRecursive method which gets the children of the object being sent in the response. getTasksRecursive does what it is supposed to, but it's running after the response is sent so the object is empty.
How do I make my asynchronous method run before the response is sent?
UPDATE: Using Aritra Chakraborty's answer, I changed it to the following and it worked.
First I separated the .map into a new function:
const getAllTasksRecursive = async(taskIds)=>{
const rtrn = await Promise.all(
taskIds.map(
async taskId=>{
return await getTaskRecursive(taskId.toString(), err=>{if(err){return {err:err}}});
}
)
)
return rtrn;
}
Then I called it in the previous function using:
result.children.tasks = await getAllTasksRecursive(tasks);
Now I am getting the data back in the response.
That's because internally a map or foreach can look something like this:
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}
It will call the callback alright, but it will not wait for the callback to complete before running the next one.
So, you need one async foreach of some sort,
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
Note, how the callback is being awaited.
Your code will have to be something like this:
const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(50)
console.log(num)
})
console.log('Done')
}
start()
Refer: https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404 This article helped me alot in the past.

Last then function being called multiple times

I'm trying to write a chain of Promises but the last .then() is being called multiple times and I don't know why. The last .then() must run a single time because it will call another API passing result as body.
I know that is being called multiple times because I'm logging as console.log().
What is wrong on my code? For my understand, then() should wait promise returns something.
app.post('/router/join', function(req, res){
let data = req.body;
sessions.validate(data)
.then(result => {
return {
authenticated: (result.code === 201)
};
})
.then(result => {
if(result.authenticated){
return contacts.getContacts(data.tenant_id).then(cs => {
let json = merge(result, cs.data);
return Promise.all(cs.data.items.map(contact => {
return messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
.then(result => {
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
});
})).then(result => {
json.items = result;
return json;
});
});
} else {
return result;
}
})
.then(result => {
//this call should run after all other promises and only a single time
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result)}
)
.catch(err => {
console.log('333');
proxy.send('invalid_session', {socket_id: data.socket_id})
})
res.status(201).send({});
});
You can use async/await to clean it up. Inside async functions you can await the results of promises.
app.post('/router/join', async function (req, res, next) {
try {
let data = req.body;
let {code} = await sessions.validate(data);
let result = { authenticated: (code === 201) };
if (result.authenticated) {
let cs = await contacts.getContacts(data.tenant_id);
let json = merge(result, cs.data);
let items = Promise.all(cs.data.items.map(async contact => {
let result = await messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
}));
json.items = items;
result = json;
}
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result);
res.status(201).send({});
} catch (err) {
proxy.send('invalid_session', {socket_id: data.socket_id})
next (err);
}
});

Resources