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);
}
});
Related
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"); //
}
I am new to node js, and am wondering if there is any way to make this more compact. I am talking specifically about the nested then catch statements. Is there anyway to try to put this into one big then catch, or just return {res:false} once rather than a bunch of repeated time for every then catch?
const uId = data.uId;
const id = data.id;
const updates = {};
updates["/shared/" + id + "/users/" + uId] = null;
updates["/users/" + uId + "/shared/" + id] = null;
return admin.database().ref().update(updates).then(() => {
let ref = "/shared/" + id
// Check if any members are left
return admin.database().ref(ref).once("value").then((snapshot) => {
var users = snapshot.val().users;
if (users == null) {
admin.database().ref(ref).remove().then(() => {
return {res: true};
}).catch(() => {
return {res: false};
});
} else {
return {res: true};
}
}).catch(() => {
return {res: false};
});
}).catch(() => {
return {res: false};
});
Return the next Promise in the chain instead of nesting them, then have a single .catch at the end:
const ref = "/shared/" + id
return admin.database().ref().update(updates)
.then(() => {
// Check if any members are left
return Promise.all([ref, admin.database().ref(ref).once("value")]);
})
.then(([ref, snapshot]) => {
var users = snapshot.val().users;
if (users == null) {
return admin.database().ref(ref).remove();
}
})
.then(() => ({ res: true }))
.then(() => ({ res: false }));
The Promise.all is needed to pass the value from one .then to another.
Using async/await would make things cleaner:
const ref = "/shared/" + id
try {
await admin.database().ref().update(updates);
const snapshot = await admin.database().ref(ref).once("value");
const { users } = snapshot.val();
if (users == null) {
await admin.database().ref(ref).remove();
}
return { res: true };
} catch (e) {
return { res: false };
}
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.
I'm trying to separate route code from database codes, but I'm stuck with an error when the route make an call to controller method.
I have the productRoutes.js:
router.route('/')
.get(async (req, res, next) => {
try {
let criteria = {};
for (const el in req.query) {
criteria[el] = req.query[el];
}
console.log('Getting all products', criteria);
const result = await controller.getAll(criteria);
console.log('router result:', result);
const status = (result.ok ? (result.count > 0 ? 200 : 404 ) : 400);
return res.status(status).send(result);
} catch (err) {
return next(err);
}
});
The productController.js:
exports.getAll = (criteria) => {
model.find(criteria, '-__v').exec((err, records) => {
const ok = (err ? false : true);
let result = {}
result.ok = ok;
if (ok) {
result.data = records;
result.count = records.length;
} else {
result.count = 0;
result.err = err;
}
console.log('controller result count:', result.count);
return result;
});
}
When executed it produces this:
Getting all products {}
router result: undefined
TypeError: Cannot read property 'ok' of undefined
at router.route.post.get (C:\dev\projects\mercante\server\routes\productRoutes.js:58:36)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
controller result count: 4
The execution isn't waiting for controller.getAll(criteria).
I've tried some codes I found looking for similar questions, like try/catch the async call, but didn't works or I missed some detail.
Thanks in advance.
The reason: Your controller's getAll method doesn't return any value.
You should do smth like this:
exports.getAll = async (criteria) => {
// Return value!
return await model.find(criteria, '-__v');
}
Thanks, #Rashad. Now it works:
productController.js:
exports.getAll = async (criteria) => {
return await model.find(criteria, '-__v');
}
productRoutes.js:
.get(async (req, res, next) => {
try {
let criteria = {};
for (const el in req.query) {
criteria[el] = req.query[el];
}
console.log('Getting all products', criteria);
let result = {};
const data = await controller.getAll(criteria);
result.count = data.length;
result.data = data;
const status = (result.ok ? (result.count > 0 ? 200 : 404 ) : 400);
return res.status(status).send(result);
} catch (err) {
return next(err);
}
});
I am having a function which does some async processing and I want to convert it to a promise, so that I can make a "chain of execution" with other depended functions later on.
Below is the code(slightly modified for privacy):
router.get('/api/prx/ptr', function(req, res) {
let prx = req.params.prx_id
let ptr = {}
let parse_text = (idx_array, prx) => {
for(let name of idx_array) {
if (typeof ptr[name] === 'undefined') {
ptr[name] = []
}
get_text(prx, name, (tg) => {
const pst = new Set(tg.ph.map(ps => ps.label))
pst.forEach(ps => {
ptr[name].push(ps)
})
})
}
return true
}
parse_text = promisify(parse_text)
documentExists(prx, function(err, dbexists){
if (err) {
console.log(err);
return res.status(404).send(err)
}
get_idx_array(prx, function(err, idx_array){
if (err) {
return res.status(err.code || 400).send(err)
}
idx_array = idx_array.map(function(v){return v._id});
parse_text(idx_array, prx)
.then(result => {
res.status(200).send(ptr)
})
})
})
})
The problem is that in the last line, the promise never resolves and thus the request-response cycle never ends. Not sure what I have done wrong in my setup. Any help appreciated.