Render result from promises.all in a view, nodeJS - javascript

Fixed: <%- locals.devices -%>
So what I'm trying to achieve, as said in the title, is sending an array of json objects result from promises.
const switch_p = new Promise(function (resolve, reject) {
conn.query('SELECT * FROM switch', (err, switch_data) => {
if (err) throw err;
resolve(switch_data);
});
});
const ap_p = new Promise etc..
...
...
resolve(ap_data);
const server_p = new Promise etc..
...
...
resolve(server_data);
const router_p = new Promise etc..
...
...
resolve(router_data);
These are the 4 promises from which I want to get the value and render them afterwards.
Promises.all function: - I've tried more cases of rendering the values but without success everytime..
This is the last try:
Promise.all([switch_p, ap_p, server_p, router_p]).then(values => {
const responses = values.map(response => values)
return Promise.all(responses)
}).then(responses => {
console.log(responses);
data = responses
console.log(data); // here its shows that data has content
res.render('admin-panel', {
layout: 'layoutAdmin',
locals: {_id: sess_id, uname: sess_uname, rol: sess_rol, devices: data}
});
});
In view I just try to show the content from devices (<%- devices -%>), which I know it'll be something like this:
[ [ [Object],
[Object],
[Object],
........
but it says "devices is not defined".. and I don't know why.
Also if there is a solution on how to do that without breaking the code in multiple functions that'll be cool.
<<<<<<<<<<<<< Edit 1 >>>>>>>>>>>>>
Let's forget about the ap_p promise for now.
This are the promises functions:
const switch_p = new Promise(function (resolve, reject) {
conn.query('SELECT * FROM switch', (err, switch_data) => {
if (err) reject(err)
else resolve(switch_data);
});
});
const server_p = new Promise(function (resolve, reject) {
conn.query('SELECT * FROM server', (err, server_data) => {
if (err) reject(err)
else resolve(server_data);
});
});
const router_p = new Promise(function (resolve, reject) {
conn.query('SELECT * FROM router', (err, router_data) => {
if (err) reject(err)
else resolve(router_data);
});
});
And the promise all function, although I don't know if I handled the error in a good way..
Promise.all([switch_p, server_p, router_p]).then(responses => {
console.log(responses);
res.render('admin-panel', {
'layout': 'layoutAdmin',
'locals': {'_id': sess_id, 'uname': sess_uname, 'rol': sess_rol, 'devices': responses}
});
})
.catch(function (error) {
console.log(error);
throw (error);
});
console.log(responses) does work fine, and the single error that I get is still "devices is not defined".

Where the promises are created, throwing does not have the desired effect. reject() must be called.
Replace ...
`if (err) throw err`;
with ...
if (err) reject(err).
In the Promsie.all() chain,
purge the first .then(...)
in the second .then(), purge data = responses; and work with responses
add a .catch() handler.
You should end up with ...
const switch_p = new Promise(function (resolve, reject) {
conn.query('SELECT * FROM switch', (err, switch_data) => {
if (err) reject(err)
else resolve(switch_data);
});
});
// etc.
and ...
Promise.all([switch_p, ap_p, server_p, router_p])
.then(responses => {
console.log(responses);
res.render('admin-panel', {
'layout': 'layoutAdmin',
'locals': { '_id': sess_id, 'uname': sess_uname, 'rol': sess_rol, 'devices': responses }
});
})
.catch(function(error) {
// Handle error here.
// Either return a value (to put promise chain back on the success path) ...
// ... or throw/rethrow error (to continue on the error path)
});

So, I found out what the problem was. I should've render <%- locals.devices -%>... yes a dumb mistake by me. Thanks you for your implication.

Related

Multiple awaits in an async function does not return [duplicate]

This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 2 years ago.
I have the following code on a server:
let tmpFileName;
// GET
app.get('/clicked', async (req, res) => {
let nullOutput = writeTmpFile("hello, world!");
await deleteTmpFile();
console.log("Hurray, finished!");
res.send({result:nullOutput});
})
function writeTmpFile(content){
tmpFileName = "tmp" + Math.random().toString() + "tsl";
return new Promise(resolve => {
fs.writeFile(tmpFileName, content, function (err) {
if (err) throw err;
console.log('Temp file creation successful.');
});
})
}
function deleteTmpFile(spec){
return new Promise(resolve => {
fs.unlink(tmpFileName, function (err) {
if (err) throw err;
console.log('Temp file deletion successful.');
});
})
}
However, in my console output, I only get
Temp file creation successful.
Temp file deletion successful.
However, if I delete await deleteTempFile(), then Hurray, finished! shows up on the console.
And more generally, how do I debug these patterns of problems?
Why is this happening?
I have rewritten your code, to showcase how to use promises.
Promise callback gets two functions as arguments: resolve and reject.
You should call resolve when operation finishes with success, and reject when it fails.
// I moved `tmpFileName` variable from here into the request handler,
// because it was "global" and would be shared between requests.
app.get('/clicked', async (req, res) => {
let tmpFileName = "tmp" + Math.random().toString() + "tsl"
let writingResult = await writeTmpFile(tmpFileName, "hello, world!")
let deletionResult = await deleteTmpFile(tmpFileName)
res.send({ writingResult, deletionResult })
console.log("Hurray, finished!")
})
function writeTmpFile (filename, content) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, content, function (err) {
// on error reject promise with value of your choice
if (err) reject(err)
// on success resolve promise with value of your choice
resolve('Temp file creation successful.')
})
})
}
function deleteTmpFile (filename) {
return new Promise((resolve, reject) => {
fs.unlink(filename, function (err) {
if (err) reject(err)
resolve('Temp file deletion successful.')
})
})
}
For working with the file you can use writeFileSync instead writeFile. (Reference).
For multiple Promise you can use the Promise.all method.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
from MDN

Promisify Express response.render method

I need to create a render method to build html blocks into a string.
It seems to work but I used the notorious "new Promise" and I wanted to know if what I've done is either correct or not:
async render(req, res) {
const locals = await this.execute(req); // DB operation, retrieve context
return new Promise((resolve, reject) => {
try {
return res.render('my-view', { ...locals, layout: null }, (err, html) => {
if (err) {
return reject(err);
}
return resolve(html);
});
} catch (err) {
return reject(err);
}
});
}
Thank you!
The new Promise constructor implicitly catches (synchronous) exceptions from the executor callback, so that try/catch is not needed. Also, the return value is ignored. You'd write just
async render(req, res) {
const locals = await this.execute(req); // DB operation, retrieve context
return new Promise((resolve, reject) => {
res.render('my-view', { ...locals, layout: null }, (err, html) => {
if (err) reject(err);
else resolve(html);
});
});
}

Using javascript promises with Mongoose queries

Normally if I was going to run multiple mongoose queries I would use the built in promise to chain them all together. In this case, the user chooses which schemas to search. This could be one of them or both. The following example uses the post data to define which schemas to search and if one is false, it should continue through the promise chain. Right now the final promise is being called before the queries.
Example in my express controller:
app.post('/custom-search', function (req, res) {
var single = false
var multi = false
if(req.body.single){
var single = true
}
if(req.body.multi){
var multi = true
}
var promise = new Promise(function (resolve, reject) {
if(multi){
multiSchema.find({}, function (err, result) {
if(!err){
console.log(result);
resolve()
}
})
}else{
resolve()
}
}).then(function (value) {
if(single){
singleSchema.find({}, function (err, result) {
if(!err){
console.log(result);
resolve()
}
})
}else{
resolve()
}
}).then(function (value) {
console.log("done");
})
})
});
output:
>done
>[singleResults]
>[multiResults]
done should be printing last so that is the first problem.
Like we discussed, few things had to be clean up. First by actually using and returning the promise for it work properly, Second, creating a mini-promise within your first .then() to resolve and reject your single conditional statement. And third, handling/catching promises.
I wrote a pseudo version of your code to illustrate my point of view, hopefully it may be of a good use.
app.get('/custom-search', function (req, res) {
// Manipulating values to test responses
var single = false;
var multi = true;
var promise = new Promise(function (resolve, reject) {
if (multi) {
setTimeout(function () {
resolve('MULTI RESOLVED!');
}, 3000);
} else {
reject('MULTI REJECTED!');
}
})
.then(function (value) {
new Promise(function (resolve, reject) {
if (single) {
setTimeout(function () {
resolve('SINGLE RESOLVED!');
}, 3000);
} else {
reject('SINGLE REJECTED!');
}
})
.catch(function (error) {
console.log('SINGLE ERROR!', error);
})
.then(function (value) {
console.log('SINGLE DONE', value);
});
})
.catch(function (error) {
console.log('MULTI ERROR!', error);
});
return promise;
});

JS: Promise doesn't return value

I need to get a value of an asynchronous function. I tried to use Promise, but that does not work:
const res = new Promise(function (resolve, reject) {
gm(readStream).size({ bufferStream: true }, function (err, size) {
if (!err) resolve(size)
})
})
console.log(res)
The result I get is Promise { <pending> }
Promises are an abstraction for callbacks, not magic. They can't make asynchronous code synchronous.
The correct solution is:
const res = new Promise(function (resolve, reject) {
gm(readStream).size({ bufferStream: true }, function (err, size) {
if (err) reject(err);
else resolve(size);
})
});
res.then(function(promiseResolutionValue) {
console.log(res)
})
You could also use async / await here:
const getSize = readStream => {
return new Promise(function (resolve, reject) {
gm(readStream).size({ bufferStream: true }, function (err, size) {
if (err) reject(err);
else resolve(size);
})
});
}
let printSize = async readStream => {
console.log(`Size is ${await getSize(readStream)}`);
}
Or, if you're using NodeJS (Version 8+), you might be able to adapt your function to use util.promisify.
Other Promise libraries, such as Bluebird, also offer such functions, to easily convert 'standard' node-style functions (functions that have a callback with err, data as arguments) into promise-returning equivalents.
Or just use the callback.
Your code should be looked like this:
const res = new Promise(function (resolve, reject) {
gm(readStream).size({ bufferStream: true }, function (err, size) {
if (!err) resolve(size)
else reject(err)
})
})
function onResolved(data) {
console.log('data: ', data);
}
function onRejected(err) {
console.log('err:', err);
}
res.then(onResolved, onRejected);
Promise does not make your code synchronous, it lets you control when you want to get the result, not immediately like callback style.

Mongoose - Query exec() never resolves in model method

In an attempt to refactor some code that will be used in different places, I moved this method which was already working from a controller to a Mongoose model.
In the model, the trackQuery.exec() never reaches the callback, never resolves. If I resolve without the exec() or without waiting for the exec(), it works fine. The trackQuery is filled properly with a Mongoose Query.
What is the subtlety about Mongoose models that I don't get ?
ArtistSchema.methods.insertRelatedTracks = function (items) {
const newTracksToSave = items.map((item) => {
return new Promise((resolve, reject) => {
const TrackModel = mongoose.model('Track'),
trackQuery = TrackModel.findOne({ externalID: item.id })
;
return trackQuery.exec((err, track) => {
if (err) {
return reject(err);
}
if (!track) {
let track = new TrackModel({
externalID: item.id,
provider: item.provider,
title: item.title,
artist: item.artist,
artistID: this._id,
artistExternalID: this.externalID,
thumbnail: item.thumbnail,
publishedAt: item.publishedAt,
count: 0
});
return track.save((err, res) => {
if (err) {
return reject(err);
}
return resolve(res);
});
}
return resolve(track);
});
});
});
return Promise.all(newTracksToSave).then(() => {
return this;
}).catch((err) => {
console.error(err);
return this;
});
}
The solution for me was to manually import the TrackModel instead of relying on the usual runtime mongoose.model('Track') method. I have no explications why mongoose.model doesn't work in this case. Any hints are welcomed.

Categories