How to decouple and encapsulate long Node.js method? - javascript

I have the following method, simplified:
var component = require('../../component.js')
exports.bigMethod = function (req, res) {
var connection = new sql.Connection(process.config.sql, function (err) {
new sql.Request(connection)
.input('input', sql.VarChar, 'value')
.execute('dbo.sp1')
.then(function (data) {
if(data[0].length === 0) {
res.status(500).send({ message: 'Some Error' });
}
// set some local variable I'll use later: localVar
new sql.Request(connection)
.input('input1', sql.Int, req.query.input1)
.input('input2', sql.Int, req.query.input2)
.input('input3', sql.DateTime2, req.query.input3)
.input('input4', sql.DateTime2, req.query.input4)
.execute('dbo.sp2')
.then(function (recordset) {
json2csv( { data: recordset[0] }, function(err, data) {
if (err) {
res.status(500).json(err);
}
fs.writeFile(localVar.path, data, function (err) {
if (err) {
res.status(500).json(err);
}
try {
var email = new component.Email();
email.set_callback(function (error) {
if (error) {
res.status(500).send({ message: 'Another error' });
}
res.jsonp([]);
});
email.send(
localVar,
{
filename: localVar.name,
path: localVar.path
}
);
} catch (e) {
res.status(500).send({ message: 'Another Error' });
}
})
});
})
.catch(function (err) {
res.status(500).send({ message: 'Another Error' });
});
})
.catch(function (err) {
res.status(500).send({ message: 'Another Error' });
});
});
};
As you can see, it's a long method, which is an anti-pattern. Furthermore, the call to sp2 is actually a duplication: there's another component that makes a similar call, only returning the result of json2csv directly.
Now, I'm not so sure how to go about dividing this method to fully take advantage of reusability. Should I encapsulate the database calls in functions returning Promises? Should I use some currying?
Thanks.

Dividing your code into different functions should be your top priority — that function alone is handling way too many things which could be easily divided.
You can make your code function much cleaner by dividing through callbacks and promises, as you already suspected.
Callback example:
new sql.Connection(..., handleSQLConnection);
handleSQLConnection(error) { ... }
Promise example:
doSomething
.then((a) => doOtherStuff(a))
.then((b) => doSmthElse(b));
doOtherStuff(a) { ... }
doSmthElse(b) { ...}
Nonetheless, refactoring is very opinionated. But you should try to avoid God functions, and instead write functions that do one thing but they do it well.

Related

How to handle async callback promise rejection?

Try to handle every exception in my async code (nodeJS, ExpressJS):
Here is almost pseudo code. I use limiter (npm limiter) module with method removeTokens (num, callback(err,remainingRequest)). Big part of code is inside the callback, and I wanna catch and throw any error there to the handler, but for now the error inside callback is still marked as "unhandled exception" and I don't understand why.
app.post('/', async (req, res) => {
try {
...
return getAll();
async function getAll () {
limiter.removeTokens(1, async (err, remainingRequest) => {
try {
throw new Error('THROWN')
} catch (error) {
throw error
}
})
}
} catch (error) {
console.log(error);
}
});
You shouldn't pass async functions into things that don't expect them (unless you catch all errors, as you are with your app.post callback). Instead, give yourself a wrapper for limiter.removeTokens that returns a promise:
function removeTokens(limiter, id) {
return new Promise((resolve, reject) => {
limiter.removeTokens(id, (err, remainingRequest) => {
if (err) {
reject(err);
} else {
resolve(remainingRequest);
}
});
});
}
(You might also look into util.promisify for that.)
Then:
app.post('/', async (req, res) => {
try {
...
await getAll(); // *** Or you might just use `removeTokens(limiter, 1)` directly here
function getAll() {
return removeTokens(limiter, 1);
}
} catch (error) {
console.log(error);
}
});
Here it is using removeTokens directly:
app.post('/', async (req, res) => {
try {
...
await removeTokens(limiter, 1);
} catch (error) {
console.log(error);
}
});
Firstly if possible please share as much code as you can as then it is easy for us to debug where the problem might be.
Coming you your question i think the problem is that in your try..catch block you are throwing the error instead of handling it with a reject. Below i have pasted a code block which you can try and let me know if it works for you. Please not the syntax might be different but the idea is that you have to reject the Promise in case of error.
`````````limiter.removeTokens(1, async (err, remainingRequest) => {
````````````try {
```````````````throw new Error('THROWN')
````````````} catch (error) {
```````````````return Promise.reject(error) //
````````````}
`````````})
``````}
```} catch (error) {
``````console.log(error);
```}
})

call the same http request if it fails but with different parameter to get default data

does it make sense to call the same http request call within the catch if the first one fails but with different parameter in order to return some default data ?
var defaultData = false;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
defaultData = true;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
console.log(err)
});
});
or this is a bad way ?
Be sure to return the new promise to the catch handler. The code will then chain properly and it avoids nesting:
clientService.getClients({defaultData:false})
.catch(function (err) {
return clientService.getClients({defaultData: true})
}).then(function (res) {
//return something
}).catch(function (err) {
console.log(err)
//IMPORTANT re-throw err
throw err;
});
This issue not bad but I think you have to find reason of the failure and on the furniture do some action.
the best is that you have to create an error handler like:
errorHandler(error:any){
///.....
}
In this method you should check status code of response, for instance if it is 500, you can't call again. or some thing like this.

avoid multiple returns looped in javascript - async / await to solve callback pyramid or callback hell,

I have this code, with a lot of blocks of returns, by example SignUp()
connectors.js
const connectors = {
Auth: {
signUp(args) {
return new Promise((resolve, reject) => {
// Validate the data
if (!args.email) {
return reject({
code: 'email.empty',
message: 'Email is empty.'
});
} else if (!isEmail(args.email)) {
return reject({
code: 'email.invalid',
message: 'You have to provide a valid email.'
});
}
if (!args.password) {
return reject({
code: 'password.empty',
message: 'You have to provide a password.'
});
}
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
resolve(createToken({ id: user._id, email: user.email }));
})
.catch((err2) => {
if (err2.code === 11000) {
return reject({
code: 'user.exists',
message: 'There is already a user with this email.'
});
}
return reject(err2);
});
});
});
},
};
module.exports = connectors;
then anoter code that call this code:
const connectors = require('./connectors');
CallsignUp(root, args) {
const errors = [];
return connectors.Auth.signUp(args)
.then(token => ({
token,
errors
}))
.catch((err) => {
if (err.code && err.message) {
errors.push({
key: err.code,
value: err.message
});
return { token: null, errors };
}
throw new Error(err);
});
}
how it's possible to avoid this in ES6 or ES7 or ES2017?
there are:
return()
.then()
return()
.then
and just loop returns:
return()
return()
return()
comming from PHP this code looks crazy, because return functions that return functions, and it's not clear for me, how is the name in javascript this type of block return code? calling functions that returns again more code?
UPDATED:
Code is not mine, full source in
https://github.com/jferrettiboke/react-auth-app-example
I'd like to understand by example:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
/src/utils/auth.js (here is encryptPassword)
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt-nodejs');
const config = require('../config');
exports.encryptPassword = (password, callback) => {
// Generate a salt then run callback
bcrypt.genSalt(10, (err, salt) => {
if (err) { return callback(err); }
// Hash (encrypt) our password using the salt
return bcrypt.hash(password, salt, null, (err2, hash) => {
if (err2) { return callback(err2); }
return callback(null, hash);
});
});
};
there are 3 returns calling functions and returning values and functions? OOP is never like this, how to use Async/Await suggest by #dashmud, i don't want to learn old things like callbacks
This code needs to be refactored in a number of ways. First, you really, really don't want to mix promises and plain async callbacks in the same logic flow. It makes a mess and wrecks a lot of the advantages of promises. Then, you have an anti-pattern going using promises inside of new Promise(). Then, you have more nesting that is required (you can chain instead).
Here's what I'd suggest:
function encryptPasswordPromise(pwd) {
return new Promise((resolve, reject) => {
encryptPassword(pwd, (err, hash) => {
err ? reject(new Error("The password could not be hashed.")) : resolve(hash);
});
});
}
const connectors = {
Auth: {
signUp(args) {
// Validate the data
let err;
if (!args.email) {
err = {code: 'email.empty', message: 'Email is empty.'};
} else if (!isEmail(args.email)) {
err = {code: 'email.invalid', message: 'You have to provide a valid email.'};
} else if (!args.password) {
err = {code: 'password.empty', message: 'You have to provide a password.'};
}
if (err) {
return Promise.reject(err);
} else {
return encryptPasswordPromise(args.password).then(hash => {
args.password = hash;
return User.create(args);
}).then((user) => {
return createToken({id: user._id, email: user.email});
}).catch(err2 => {
if (err2.code === 11000) {
throw new Error({code: 'user.exists', message: 'There is already a user with this email.'});
} else {
throw err2;
}
});
}
}
}
};
Summary of Changes:
Collect all errors initially and do return Promise.reject(err) in one place for all those initial errors.
Promisify encryptPassword() so you aren't mixing regular callbacks with promise logic flow and you can then properly return and propagate errors.
Removing wrapping of the whole code with return new Promise() since all your async operations are now promisified so you can just return promises directly. This really helps with proper error handling too.
Undo unneccessary nesting and just chain instead.
Remove Object.assign() as there did not appear to be a reason for it.
In your edit, you asked for an explanation of this code segment:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
This code calls encryptPassword().
It returns the result of that which is probably undefined so all the return is doing is controlling program flow (exiting the containing function), but since there is no code after that return at the same level, that's unnecessary.
You pass a callback to encryptPassword(). That callback is asynchronous, meaning it is called some time later.
The callback gets two arguments: err and hash. In the node.js async calling convention, if the first argument is truthy (e.g. contains some truthy value), then it represents an error. If it is falsey (usually null), then there is no error and the second argument contains the result of the asynchronous operation.
If there was an error, the parent promise is rejected and the callback is exited. Again, there is no return value from reject so the return is just for exiting the callback at that point (so no other code in the callback runs).
Then, another asynchronous operation User.create() is called and it is passed the args object with a new password set in it.
User.create() returns a promise so its result is captured with a .then() method and a callback passed to that.
Some time later, when the asynchronous User.create() finishes, it will resolve its promise and that will cause the .then() callback to get called and the result will be passed to it. If there's an error in that operation, then the .then() callback will not be called, instead the .catch() callback will be called. In that .catch() callback, the parent promise is rejected. This is a promise anti-pattern (resolving a parent promise inside another promise) because it is very easy to make mistakes in proper error handling. My answer shows how to avoid that.
Extending #jfriend00's answer, here's an approach that uses the ES2017 async / await syntax to flatten the callback pyramid or callback hell, however you prefer to call it:
const encryptPasswordPromise = require('util').promisify(encryptPassword)
const connectors = {
Auth: {
async signUp (args) {
const { email, password } = args
// Validate the data
let err
if (!email) {
err = { code: 'email.empty', message: 'Email is empty.' }
} else if (!isEmail(email)) {
err = { code: 'email.invalid', message: 'You have to provide a valid email.' }
} else if (!password) {
err = { code: 'password.empty', message: 'You have to provide a password.' }
}
if (err) {
throw err
}
let hash
try {
hash = await encryptPasswordPromise(password)
} catch (err) {
throw new Error('The password could not be hashed.')
}
const { _id: id, email } = await User.create(Object.assign(args, { password: hash }))
try {
return createToken({ id, email })
} catch (err) {
if (err.code === 11000) {
throw { code: 'user.exists', message: 'There is already a user with this email.' }
} else {
throw err
}
}
}
}
}
module.exports = connectors
Rather than handwrite my own promisified promise-based encryptPassword(), I opted to use a node.js builtin transformation function for that called util.promisify().
Overall, there are still a few improvements that could be made like moving the validation of the signUp() arguments to a separate function, but none of that is related to flattening the "callback hell" anti-pattern that gave rise to promise-based control-flow and async/await syntax.
First things first. There are no loops in your code.
If you're coming from PHP, then I would guess Javascript's asynchronous execution model looks alien to you. I would suggest learning more about Javascript.
Learn the following in the following order:
Callbacks
Promises
Generators
Async/Await
Those are the different approaches on how to handle Javascript's asynchronous execution model starting from the most basic (callbacks) to the most modern approach, "async/await".
Async/await would be the most readable but it would be better if you understand how async/await came to be since they are easy to use the wrong way if you don't understand what you're doing.

Flow of logic when using promises

contrived example
Say we need to construct a Foo and Bar for a User in some Node application. We are expected to implement the constructFooForUser function, which must invoke some callback function with either
an Error
a null error, the created Foo, and the created Bar
To do this, we must make use of some database functions which fetch/create our objects, returning a Promise.
var constructFooForUser = function(userId, data, callback) {
db.users.find(userId)
.then(function(user) {
if (!user) { return callback(new Error('user not found')); }
db.foos.create(user, data)
.then(function(foo) {
db.bars.create(user, foo, data)
.then(function(bar) {
callback(null, foo, bar);
})
.catch(function(err) {
callback(err);
});
})
.catch(function(err) {
callback(err);
});
})
.catch(function(err) {
callback(err);
});
};
Is this the correct way to structure this type of promise based code?
I've seen examples of Promise code which looks like
doSomething()
.then(doSomethingElse)
.then(doYetAnotherThing)
.then(doLastThing)
.catch(handleError);
but I don't think this works in this scenario, as I need user, foo, and bar at the same time, and scope isn't shared between the chained functions.
I ask because the code looks repetitive, so I am wondering if something is unusual.
EDIT: Forgot to include foo in the creation of bar
How about storing foo in some variable until we need it?
var constructFooForUser = function(userId, data, callback) {
var gFoo;
db.users.find(userId)
.then(function(user) {
if (!user) { return callback(new Error('user not found')); }
return db.foos.create(user, data)
})
.then(function(foo) {
gFoo = foo;
return db.bars.create(user, data)
})
.then(function(bar) {
callback(null, gFoo, bar);
})
.catch(function(err) {
callback(err);
});
};
I typically (and yes I consider this a hacky workaround) accumulate the results in an array as I go.
firstThing()
.then(first => Promise.all([first, secondThing]))
.then(([first, second]) => Promise.all([first, second, thirdThing()]))
.catch(e => handleErr(e));
This accumulation pattern works, and with ES 2015 destructuring its not too clunky.
foo and bar don't seem to depend on each other, so we could make this part paralell:
var constructFooForUser = function(userId, data, callback) {
db.users.find(userId).then(function(user) {
//because rejecting a promise means nothing else than throwing an Error
if(!user) throw new Error('user not found');
//resolves as soon as both requests have been resolved.
//rejects if _any_ of the requests is rejecting
return Promise.all([
db.foos.create(user, data),
db.foos.create(user, data)
]);
}).then(
([foo, bar]) => callback(null, foo, bar), //utilizing array destructuring
(err) => callback(err)
);
}
I'm using .then(a, b) instead of .then(a).catch(b), because the latter would also catch Errors that are thrown in your callback-function (and we don't want that); that's your responsibility to deal with them. It would hide bugs in your code, by error-messages simply not showing up in the console.
Even your attempt with the nested promises could be streamlined by returning the promises and concatenating at least the error-part
var constructFooForUser = function(userId, data, callback) {
db.users.find(userId)
.then(function(user) {
if (!user) throw new Error('user not found');
return db.foos.create(user, data)
.then( foo => db.bars.create(user, data).then( bar => callback(null, foo, bar) ) );
})
.catch(function(err) {
callback(err);
});
};

Chaining promises with then and catch

I'm using the bluebird Promise library. I'd like to chain promises and catch specific promises errors. Here's what I'm doing :
getSession(sessionId)
.catch(function (err) {
next(new Error('session not found'));
})
.then(function (session) {
return getUser(session.user_id);
})
.catch(function (err) {
next(new Error('user not found'));
})
.then(function (user) {
req.user = user;
next();
});
But if an error is thrown by getSession, the two catch are called, as well as the second then. I'd like to stop the error propagation at the first catch, so that the second catch is only called when getUser throws, and the second then when getUser succeeds. What do?
The promise that is returned by the .catch method will still be resolved with the result of the callback, it doesn't just stop the propagation of the chain. You will either need to branch the chain:
var session = getSession(sessionId);
session.catch(function (err) { next(new Error('session not found')); });
var user = session.get("user_id").then(getUser);
user.catch(function (err) { next(new Error('user not found')); })
user.then(function (user) {
req.user = user;
next();
});
or use the second callback to then:
getSession(sessionId).then(function(session) {
getUser(session.user_id).then(function (user) {
req.user = user;
next();
}, function (err) {
next(new Error('user not found'));
});
}, function (err) {
next(new Error('session not found'));
});
Alternatively, the better way would to just propagate the errors through the chain, and call next only in the very end:
getSession(sessionId).catch(function (err) {
throw new Error('session not found'));
}).then(function(session) {
return getUser(session.user_id).catch(function (err) {
throw new Error('user not found'));
})
}).then(function (user) {
req.user = user;
return null;
}).then(next, next);
Since you're using bluebird for promises, you actually don't need a catch statement after every function. You can chain all your thens together, and then close the whole thing off with a single catch. Something like this:
getSession(sessionId)
.then(function (session) {
return getUser(session.user_id);
})
.then(function (user) {
req.user = user;
next();
})
.catch(function(error){
/* potentially some code for generating an error specific message here */
next(error);
});
Assuming the error messages tell you what the error is, it's still possible to send an error specific message like 'session not found' or 'user not found', but you'll just have to look into the error message to see what it gives you.
Note: I'm sure you probably have a reason for calling next regardless if there's an error or not, but it might be useful to throw in a console.error(error) in the case that you get an error. Alternatively, you could use some other error handling function, whether it's a console.error, or res.send(404), or something of the like.
I am using it like that:
getSession(x)
.then(function (a) {
...
})
.then(function (b) {
if(err){
throw next(new Error('err msg'))
}
...
})
.then(function (c) {
...
})
.catch(next);

Categories

Resources