How to perform a callback in a promise? - javascript

I have here a function which gets a Section. It returns a Promise
router.get('/menu_section', (req, res) => {
Section.read(req.body)
.then(d => {
send(d, res);
})
.catch(e => {
error(e, res);
});
});
Is there a way, that while I handle the Promise I can cut down on the then catch boilerplate code? I am looking to write it in this way to reduce boiler plate.
router.get('/menu_section', (req, res) => {
Section.read(req.body).respond(data, err, res)
});
//handle it in this way for all routes.
function respond(data, err, res){
if(err) res.data({err})
else res.json({data});
}
EDIT: I want to avoid writing then catch altogether for every Promise handle

You wouldn't be able to do exactly what you mentioned (without overriding Promise, which is generally frowned upon).
You could however create a simple wrapping function to do it for you:
function respond(promise, res) {
promise
.then(data) => res.data(data))
.catch(err => res.data({err})
}
router.get('/menu_section', (req, res) => {
respond(Section.read(req.body), res);
});
You could even boil this down a bit to something like this:
function respond(getData) {
return (req, res) => {
getData(req)
.then(data) => res.data(data))
.catch(err => res.data({err})
};
}
router.get('/menu_section', respond(req => Section.read(req.body)));
With the second approach, you're basically just providing a function which gets the data, then it'll take that and process it in a standard way. It'll also create a function for taking req and res itself.

Maybe with currying?:
const respond = res => data => {
res.json(data);
};
So you can do:
router.get('/menu_section', (req, res) => {
Section.read(req.body).catch(e => e).then(respond(res));
});
However then i would directly make a middleware out of it:
const respond = (req, res, next) => {
Section.read(req.body).then(
data => res.json(data),
err => next(err)
);
};
So you can do
router.get('/menu_section', respond);
Note that async / await can be helpful here:
router.get('/menu_section', async (req, res) => {
try {
res.json(await Section.read(req.body));
} catch(e) {
res.json(e);
}
});

If you're looking to respond to all routes in this manner, you could use a Middleware function and use it application wide.
app.use((req, res, next) => (
Section.read(req.body)
.then(d => send(d, res))
.catch(e => error(e, res));
)
You can also use such function on a per-route basis, or even per-file (containing multiple routes).

Related

Can't get result from a nodejs module

I wrote a small Javascript module to get rows from a MongoDB database:
async function getAll() {
await mongoose.connect(config.mongoURL).catch(err => { console.log(err); });
const conn = mongoose.connection;
conn.collection('healthdata')
.find({}).toArray().then(result => {
console.log('=>Docs:', result);
return result;
}).catch (err => {
console.log(err);
}).finally(() => {
conn.close();
});
}
exports.getAll = getAll
I get the correct display from the console ("=>Docs:").
But, when I want to get the result from the following calling function, I get the Undefined value ("Result:"):
app.get("/", async (req, res) => {
var docs = await db.getAll().then();
console.log("Result:", docs);
res.render("index.html");
});
What is missing? Wrong?
you should not mix promises with async/await, so either the answer provide by G-Force but remove the await before db.getAll() or use var docs = await db.getAll(); this should solve your problem.
Bro if you are using mongoose follow these to get data from mongoose schema
schema.find().exec().then(result=>{
console.log(result)
}).catch(error=>{
console.log(error)
})
here all schema data in result that we console if any error come it displays in console.log(error)
I think the console.log(Result:...) line is executing before your getAll completes.
I would wrap it inside the "then" clause:
app.get("/", async (req, res) => {
const docsReturned = await db.getAll()
.then(docs => {
console.log("Result:", docs);
res.render("index.html");
})
.catch(err => {
console.error(err);
}));
return(docsReturned);
I also suggest adding a catch for any errors...good practices. :)

Do we need to wrap app.get in async function

I've come across this code
(async () => {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
more endpoints here
....
})();
I don't get why we need to wrap app.get in async here, is that necessary?
Probably not, but we'd need more context to be sure. As shown, no, there's no point.
It may be that they were relying on information they only got asynchronously for setting up the routes and wanted to use await rather than .then/.catch when using that information, e.g.:
(async () => {
try {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
const moreRoutes = await getMoreRoutes();
// −−−−−−−−−−−−−−−−^^^^^
for (const route of moreRoutes) {
// ...set up more routes
}
} catch (e) {
// Handle/report error
}
})();
If so, hopefully they have a try/catch around the entire body (as shown above) or a .catch at the end like this:
(async () => {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
const moreRoutes = await getMoreRoutes();
// −−−−−−−−−−−−−−−−^^^^^
for (const route of moreRoutes) {
// ...set up more routes
}
})().catch(e => {
// Handle/report error
});
async/await can make it much easier (IMHO) to read code. But the above can be done with .then/.catch as well:
app.get('/health', (req: Request, res: Response) => {
res.send();
});
getMoreRoutes()
.then(moreRoutes => {
// ...set up more routes
})
.catch(e => {
// Handle/report error
});

axios.get call returning "response.data.map is not a function"

I am trying to request JSON data and import it into a certain part of my website. When I console log it I have no issues. However when I attempt to assign the data to a array and variable it says "TypeError: response.data.map is not a function"
the code right below works in console.
router.get("/", (req, res, next)=>{
axios.get("http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json")
.then(res => console.log(res))
.catch((err)=>{
console.log(err);
});
res.render("champions");
});
Once I add the array it no longer works
router.get("/", (req, res, next)=>{
axios.get("http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json")
.then( (response)=>{
let championArray = [];
response.data.map((champs)=>{
championArray.push(champs);
});
res.render("champions", {
champs: championArray
});
})
.catch((err)=>{
console.log(err);
});
});
Once I assign the array it no longer works. I am trying to get this script to assign each champion to a variable so I can call it from the front end.
Since the API is returning array as objects, .map will not work. Try below code, it will work.
Object.keys(response.data).map((champs)=>{
championArray.push(champs);
});
router.get("/", (req, res, next)=>{
axios.get("http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json")
.then( (response)=>{
responseData = JSON.parse(response.data); //or just response idk :)
let championArray = [];
responseData.map((champs)=>{
championArray.push(champs);
});
res.render("champions", {
champs: championArray
});
})
.catch((err)=>{
console.log(err);
});
});
Try using the JSON.parse to create a JavaScript object out of the JSON response, note that this will only work if the request you make has a Content-type: application/json, otherwise try JSON.stringify. but the .map function is usually used on arrays not objects
Let me know if that helped
router.get("/", (req, res, next)=>{
axios.get("http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json")
.then( (response)=>{
let championArray = [];
let resData = response.data;
if (!resData.length){
resData.map((data, index) => {
if (!data){
data = JSON.parse(data);
championArray.push(data);
}
})
}
res.render("champions", {
champs: championArray
});
})
.catch((err)=>{
console.log(err);
});
});
The API response is an object. But .map is a function on arrays, which is why you are getting the error response.data.map is not a function.
Following are an option for you.
If you're using ES5,
Object.keys(response.data).forEach(function(key) {
console.log(key, response.data[key]);
});
using ES6, you could use for loop
for (const key of Object.keys(response.data)) {
console.log(key, response.data[key]);
}
in both cases, key would be the object key (in your case, the names) and response.data[key] would give you the data for that key.
Also note that since you're only interested in the values and not keys, you could simply use
Object.values(response.data)
Your final code would look like
router.get("/", (req, res, next)=>{
axios.get("http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json")
.then( (response)=>{
res.render("champions", {
champs: Object.values(response.data)
});
})
.catch((err)=>{
console.log(err);
});
});

Rewrite Code Form Callback to Async Await

I have been trying to convert my existing Express JS code from function callback to Async Await.
The function callback code is
app.get('/contactlist', (req, res) => {
db.contactlist.find((err, docs) => {
res.json(docs);
});
});
And the Async Await code is
app.get('/contactlist', async (req, res) => {
db.contactlist.find(async(err, docs) => {
res.json(await docs);
});
});
The new code works good and I have strong feeling that, it is not the right Implementation of Async Await
You're still using callbacks. You can only switch to async/await, if the API returns promises. app.get() does not return promises, so the following doesn't work with Express:
let (req, res) = await app.get('/contactlist');
try {
let docs = await db.contactlist.find();
res.json(docs);
} catch (err) {
// Handle the error
}
All of that would have to be inside an async function.
What is db in your example? If db.contactlist.find returns a promise, you can use async/await like this:
app.get('/contactlist', async (req, res) => {
try {
let docs = await db.contactlist.find();
res.json(docs);
} catch (err) {
// Handle the error
}
});
you did not convert the callback to async await at all,callback is still there.The right implementation would look like this
app.get('/contactlist', async (req, res) => {
const docs = await getContactList();
res.json(docs)
});
function getContactList() {
let promise = new promise(function(resolve, reject) {
db.contactlist.find({}, function(err, results) {
if (err)
reject(err)
else
resolve(results)
});
});
return promise
}
^EDITS: Here's the complete example, if you have a custom function against await, you can promisify that function by creating and returning a promise in that function

res.render ONLY after multiple data queries have finished

On my app.get in my server.js, I am returning a data set from mongo db, and then rendering my page whilst passing the data to it.
as shown bellow:
//page load
app.get('/', (req, res) => {
//find all data in test table
var articles;
db.collection('test').find().toArray((err, result) => {
if (err) return console.log(err)
articles = result
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: articles
})
})
})
I want to be able to put multiple db queries, and then pass multiple variables of data to my render, my problem is that when I put my res.render outside of a db query, it tries to render before the db gets its data set.
see what I tried bellow:
//page load
app.get('/', (req, res) => {
//find all data in test table
var articles;
db.collection('test').find().toArray((err, result) => {
if (err) return console.log(err)
articles = result
})
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: articles
})
})
my question is:
How could I make sure that the render happens ONLY after my db queries have run and returned the data to a variable?
Ultimately I would want to be able to do something like this:
//page load
app.get('/', (req, res) => {
//find all data in table 1
var AAA;
db.collection('test1').find().toArray((err, result) => {
if (err) return console.log(err)
AAA = result
})
//find all data in table 2
var BBB;
db.collection('test2').find().toArray((err, result) => {
if (err) return console.log(err)
BBB = result
})
//find all data in table 3
var CCC;
db.collection('test3').find().toArray((err, result) => {
if (err) return console.log(err)
CCC = result
})
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: AAA,
quotes2: BBB,
quotes3: CCC
})
})
Any help or advice on this is appreciated. Thank you in advance.
Try this code, it's not tested but I think it should work:
app.get('/', (req, res) => {
var list = {};
db.collection('test').find().toArray().then(result => {
list.result = result;
}).then(() => {
return Promise.resolve(db.collection('foo').find().toArray());
}).then(result2 => {
list.result2 = result2;
}).then(() => {
return Promise.resolve(db.collection('bar').find().toArray());
}).then(result3 => {
list.result3 = result3;
}).then(() => {
res.render('./pages/index.ejs', list);
}).catch(e => {
console.error(e);
});
});
Update: we can use async/await for any method that returns Promise so the code would be cleaner:
// you can use express-async-errors package to make routes async
app.get('/', async(req, res) => {
let list = [];
list.result = await db.collection('test').find().toArray();
list.result2 = await db.collection('foo').find().toArray();
list.result3 = await db.collection('bar').find().toArray();
res.render('./pages/index.ejs', list);
});
You can use async module for that. Use async.parallel for functions which are independant of each other and use async.series for dependent processes. Check async.

Categories

Resources