How to avoid redundant code writing promises? - node.js

I have a file where multiple export functions that are invoked from api's and each of these methods will make some get/post inside the function.
so my questions is for Promise.all that looks redundant to me is there better approach to achieve this using one private method handler that can be implemented or invoked from each of those export function and return response.
main.ts
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}

You can do exactly what you wrote - create function that does the general handling.
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
sendResponse(req, res, [p1,p2]);
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
sendResponse(req, res, [p1,p2]);
}
function sendResponse(req, res, promises) {
Promise.all(promises)
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
PS: You should have some res handling in .catch (res.end() or res.status(500); res.json({error: e})) otherwise the request will be hanging on for 30-90sec (based on your settings)

In case p1, etc. promises are really created Promise.resolve, it could be omitted; Promise.all accepts regular values.
It could be written with async..await in more succinct manner:
export async function getUser(req: Request, res: Response) {
...
try {
const results = await Promise.all([p1, p2]);
res.json(results);
} catch (e) {
console.log(e)
}
}
At this point functions don't need to be DRYed any further.

Related

Nest multiple async await

I have the following Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
await all({'p1': p1, 'p2': p2}).then((response) => {
...
console.log("Response:",
ruleCtrl.manageRule(detection, res);
});
});
ruleCtrl.manageRuleis as follows:
export async function manageRule(identifierDetected, res) {
let rule = db.getRule(identifierDetected);
await all([rule]).then((ruleExtracted) => {
...
res.json(ruleExtracted);
}).catch((err) => {
res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
});
}
and db.getRule:
export async function getRule(idRule) {
return new Promise((resolve, reject) => {
Rule.findOne({ruleID: idRule}, (err, rule) => {
if (err) {
reject("MongoDB Rule error: " + err);
} else {
resolve(rule);
}
});
})
}
My response is into manageRule and this function depends of the values extracted into the await all. So, right now, Express is returning a response before get the information from mongoose database (db).
Which is the way to handle this issue?
Thanks everyone!
I would refactor your code a bit to make it easier to read, and also return the result from ruleCtrl.manageRule(detection, res);.
The request might simply be timing out since your original code is missing a return there or an await (to make sure it finishes executing)
Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
// Catch any exceptions from the promises. This is the same as using .catch
try {
// Lets assign the returned responses to variable
let [p1Result, p2Result] = await all({'p1': p1, 'p2': p2});
...
console.log("Responses:", p1Result, p2Result);
// return the response from manageRule method
return ruleCtrl.manageRule(detection, res);
} catch(err) {
// Handle err here
}
});
One of the great benefits with async await is moving away from chained promises, so simply return the result from the await to a variable instead of using .then()
ruleCtrl.manageRule
export async function manageRule(identifierDetected, res) {
// Use try catch here to catch error from db.getRule. Assign to variable and return
// res.json
try {
let ruleExtracted = await db.getRule(identifierDetected);
...
return res.json(ruleExtracted);
} catch(err) {
return res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
}
}
You dont have to return res.json or res.status here, I just like to keep track of when I want to end function execution.
You could refactor the ruleCtrl.manageRule method even further by not sending in res as a parameter but by returning the result from db.getRule instead. Let router.post('/verify) handle req and res, so to make it even easier to read.

Higher Order Functions with Express Routes

I was looking at a blog https://www.acuriousanimal.com/2018/02/15/express-async-middleware.html on how to handle do await/async in Typescript express routes.
I liked the idea of using the Higher Order Functions to avoid code redundancy, but
am not sure how to do it in TypeScript
Is there any other better way TypeScript recommends for this kind of scenario
Sample Code snippet:
https://gist.github.com/kameshsampath/e550d5bf19feb9b59d0ec1871e59b53a
I believe you want to remove the catch from the following code into a higher order function:
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
});
This does that:
const asyncHandler = fn => (req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch((err)=>res.status(404).send(err);)
app.get("/api/frames", asyncHandler(async (req: Request, res: Response, next: any) => {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
}));

Using async await properly in node js

To overcome callback hell in javascript, I'm trying to use async await from legacy code written in SQLServer procedure.
But I'm not sure my code might be write properly.
My first confusing point is when async function returns, should it return resolve() as boolean, or just return reject and handle with try-catch?
Here is my code snippets.
Please correct me to right direction.
apiRoutes.js
app.route('/api/dansok/cancelDansok')
.post(dansokCancelHandler.cancelDansok);
dansokCancelController.js
const sequelize = models.Sequelize;
const jwt = require('jsonwebtoken');
async function jwtAccessAuthCheck(accessToken) {
if (!accessToken) {
return Promise.reject('Empty access token');
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
return Promise.reject('TokenExpiredError.');
} else {
return Promise.resolve();
}
});
}
async function checkFeeHist(dansokSeqNo) {
let feeHist = await models.FeeHist.findOne({
where: { DansokSeqNo: dansokSeqNo}
});
return !!feeHist;
}
async function getNextDansokHistSerialNo(dansokSeqNo) {
....
}
async function getDansokFee(dansokSeqNo) {
....
}
async function doCancel(dansokSeqNo) {
try {
if (await !checkFeeHist(dansokSeqNo)) {
log.error("doCancel() invalid dansokSeqNo for cancel, ", dansokSeqNo);
return;
}
let nextDansokSerialNo = await getNextDansokHistSerialNo(dansokSeqNo);
await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo);
await updateDansokHist(dansokSeqNo);
await updateVBankList(dansokSeqNo, danokFee.VBankSeqNo);
await getVBankList(dansokSeqNo);
} catch (e) {
log.error("doCancel() exception:", e);
}
}
exports.cancelDansok = function (req, res) {
res.setHeader("Content-Type", "application/json; charset=utf-8");
const dansokSeqNo = req.body.DANSOKSEQNO;
const discKindCode = req.body.HISTKIND;
const worker = req.body.PROCWORKER;
const workerIp = req.body.CREATEIP;
const accessToken = req.headers.accesstoken;
//check input parameter
if (!dansokSeqNo || !discKindCode || !worker || !workerIp) {
let e = {status:400, message:'params are empty.'};
return res.status(e.status).json(e);
}
try {
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
doCancel(dansokSeqNo).then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
});
});
} catch(e) {
return res.status(e.status).json(e);
}
};
You'll need to rewrite jwtAccessAuthCheck(accessToken) so that it keeps track of the outcome of its nested tasks. In the code you've written:
// Code that needs fixes!
async function jwtAccessAuthCheck(accessToken) {
// This part is fine. We are in the main async flow.
if (!accessToken) {
return Promise.reject('Empty access token');
}
// This needs to be rewritten, as the async function itself doesn't know anything about
// the outcome of `jwt.verify`...
jwt.verify(accessToken,"dipa",function(err){
if(err) {
// This is wrapped in a `function(err)` callback, so the return value is irrelevant
// to the async function itself
return Promise.reject('TokenExpiredError.');
} else {
// Same problem here.
return Promise.resolve();
}
});
// Since the main async scope didn't handle anything related to `jwt.verify`, the content
// below will print even before `jwt.verify()` completes! And the async call will be
// considered complete right away.
console.log('Completed before jwt.verify() outcome');
}
A better rewrite would be:
// Fixed code. The outcome of `jwt.verify` is explicitly delegated back to a new Promise's
// `resolve` and `reject` handlers, Promise which we await for.
async function jwtAccessAuthCheck(accessToken) {
await new Promise((resolve, reject) => {
if (!accessToken) {
reject('Empty access token');
return;
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
reject('TokenExpiredError.');
} else {
resolve();
}
});
});
// We won't consider this async call done until the Promise above completes.
console.log('Completed');
}
An alternate signature that would also work in this specific use case:
// Also works this way without the `async` type:
function jwtAccessAuthCheck(accessToken) {
return new Promise((resolve, reject) => {
...
});
}
Regarding your cancelDansok(req, res) middleware, since jwtAccessAuthCheck is guaranteed to return a Promise (you made it an async function), you'll also need to handle its returned Promise directly. No try / catch can handle the outcome of this asynchronous task.
exports.cancelDansok = function (req, res) {
...
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
return doCancel(dansokSeqNo);
})
.then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
})
.catch(e => {
res.status(e.status).json(e);
});
};
I strongly suggest reading a few Promise-related articles to get the hang of it. They're very handy and powerful, but also bring a little pain when mixed with other JS patterns (async callbacks, try / catch...).
https://www.promisejs.org/
Node.js util.promisify

Wrap async await queries in try catch

I originally had try...catch in my getAllUsers method for querying but ended up removing it because as far as I could tell it wasn't doing anything. I know the async function returns a promise so it should be fine and actually based on how the code is structured I think it's required otherwise the try...catch in the query would swallow the error. Is there anything I'm missing with this structure and use of async/await, try...catch, and .then .catch?
let getAllUsers = async () => {
let res = await models.users.findAll({
attributes: [ 'firstName', 'lastName' ]
});
return res;
};
router.get(`${path}`, (req, res) => {
queries.users.getAllUsers()
.then(users => {
res.status(200).json(users);
})
.catch(error => {
res.status(500).send(error)
});
});
There's just no reason to use await at all in your function. Instead of this:
let getAllUsers = async () => {
let res = await models.users.findAll({
attributes: [ 'firstName', 'lastName' ]
});
return res;
};
It can just be this:
let getAllUsers = () => {
return models.users.findAll({
attributes: [ 'firstName', 'lastName' ]
});
};
You just return the promise directly and the caller uses the promise the same as you already were. Since you are not using the result within your getAllUsers() function or coordinating it with anything else, there's no reason to use await. And, since there's no use of await, there's no reason for the function to be declared async either.
If you wanted to use await, you could use it for the caller of getAllUsers() like this:
router.get(`${path}`, async (req, res) => {
try {
let users = await queries.users.getAllUsers();
res.status(200).json(users);
} catch(error => {
res.status(500).json(error);
}
});
And, here you would have to use try/catch in order to catch rejected promises. Personally, I don't see how this is particularly better than what you had originally with .then() and .catch() so for a situation as simple as this (with no coordination or serialization with other promises), it's really just a matter of personal preference whether to use .then() and .catch() or await with try/catch.
You would use async/await with the code that calls getAllUsers rather than using it in getAllUsers itself:
const getAllUsers = () => {
return models.users.findAll({
attributes: [ 'firstName', 'lastName' ]
});
};
router.get(`${path}`, async (req, res) => {
try {
const users = await queries.users.getAllUsers();
res.status(200).json(users);
} catch (error) {
res.status(500).send(error)
}
});
The best way I have found to handle this is using middleware.
Here is the function:
// based upon this
// http://madole.xyz/error-handling-in-express-with-async-await-routes/
// https://github.com/madole/async-error-catcher
export default function asyncErrorCatcher(fn) {
if (!(fn instanceof Function)) {
throw new Error("Must supply a function");
}
return (request, response, next) => {
const promise = fn(request, response, next);
if (!promise.catch) {
return;
}
promise.catch((error) => {
console.log(error.message);
response.sendStatus(500);
});
};
}
Here is the usage:
router.get("/getSettings/", asyncErrorCatcher(async (request: Request, response: Response) => {
const settings = await database.getSettings();
response.json(settings);
}));

Nodejs - Async/Await to my controller

Right now i have this route controller
export let remove = (req: Request, res: Response) => {
Area.removeAndRecalc(req.query.ids).then(() => {
return res.json({ success: true });
});
};
and calls the following model method
areaSchema.statics.removeAndRecalc = async function(ids) {
let parents = await this.model("Area").find({_id: ids});
await this.model("Area").delete(
{
_id: ids
}
);
await parents.forEach((parent) => {
parent.recalcParentChilds();
})
return;
}
The function returns a promise. Is it possible to write this code inside the controller? I try to use "async" to my controller but i doesn't work
Something like this (doesn't work)
export let remove = async (req: Request, res: Response) => {
let parents = await this.model("Area").find({_id: req.query.ids});
await this.model("Area").delete(
{
_id: req.query.ids
}
);
await parents.forEach((parent) => {
parent.recalcParentChilds();
})
return res.json({ success: true });
};
It's not clear "what" exactly doesn't work. An exception or description of the bad behaviour would be useful.
But check out async-middleware. I'm using it like this, for example in some projects:
import { wrap } from 'async-middleware'
//Loads of code, some of which creates router, an Express router.
router.get('/some/path/here', wrap(async (req, res) {
var data = await dataFromService();
res.write(renderData(data));
res.end();
}));
//Loads of code
I think there is a mistake with forEach in the remove function , forEach returns undefined not a promise so await won't work as expected, try to something like this :
export let remove = async (req: Request, res: Response) => {
//...
for (let i=0; i< parent.length;i++){ // <-- use for loop instead of forEach
await parent.recalcParentChilds();
}
return res.json({ success: true });
};

Resources