First of all, I’m not sure whether the topic belongs to StackOverflow or not. However, I’ve found some discussions regarding other programming languages that addresses the topic on StackOverflow but not for Javascript. So, I’m just going to throw my question into the ring:
What is the best way/pattern to handle a possible „not found“ scenario in hypothetical GetUserByUsername function in Javascript.
Why I’m asking? I want to follow a certain coding standard throughout my project.
My more specific scenario the following:
- I have class User
- I have a list of user which contains instances of the class User
- I look up for a certain user but this user doesn’t exist in the list
How should I handle that “not find” situation in my regarding function?
I found the following approaches:
- Return undefined
- RORO (Receive an object, return an object)
- Null object pattern
- Throwing a custom error
So what do I mean exactly. Taking the following example class User.
module.exports = class User {
constructor() {
this.userName = null,
this.name = null,
this.age = null
}
...
getName() {
console.log(this.name);
}
}
Somewhere in my code I have a function that provides the functionality to return a certain user by its user name from a list of users. That list is an array what consists of instances of the class User.
A simple implementation could like that:
{
...
getUserByUsername(pUserName) {
/* listOfUsers array with objects of the class User */
var lUser = this.listOfUsers.find(user => user.userName === pUserName);
return lUser;
}
...
}
In some case it could be possible that the passed user name doesn’t belong to a user. Therefore no user is found.
How could I handle it?
1.) Return undefined - like the example code does.
/* We expect "Unknown" is not a user name in that list*/
var lRequestedUser = getUserByUsername("Unknown");
/* Check if requested user is undefined. Note: I'm using lodash for staff like that */
if(_.undefined(lRequestedUser)) {
//Handling
}
lRequestedUser.getName();
2.) RORO (Receive an object, return an object) I’m passing an object to my function and I’m returning an object as result. Within the object I’m returning, I have an indicator what shows if the operation was successful.
getUserByUsername(pParams) {
if(_.undefined(pParams.userName)) {
//Throw custom error
}
var lReturnObject = {
isSuccessfull: false,
data: null
}
/* listOfUsers array with objects of the class User */
var lUser = this.listOfUsers.find(user => user.userName === pParams.userName);
if(!(_.undefined(lUser))) {
lReturnObject.isSuccessfull = true;
lReturnObject.data = lUser
}
return lUser;
}
...
}
/*
Building a object which is passed to the function. Username is a member of that object
*/
var lRequest = {
userName: "Unknown"
}
/* We expect "Unknown" is not a user name in that list*/
var lRequestedUser = getUserByUsername(lRequest);
if(!(lRequestedUser.isSuccessfull)) {
//Handling
}
lRequestedUser.data.getName();
3.) Null object pattern The thing I don’t like about that solution is the fact, that I always have to enhance the class for the null objects if the main class gets an additional function.
module.exports = class NullUser {
constructor() {
this.userName = null,
this.name = null,
this.age = null
}
...
getName() {
console.log(this.name);
}
}
{
...
getUserByUsername(pUserName) {
/* listOfUsers array with objects of the class User */
var lUser = this.listOfUsers.find(user => user.userName === pUserName);
if(_.undefined(lUser)) {
return new NullUser();
}
return lUser;
}
...
}
/* We expect "Unknown" is not a user name in that list*/
var lRequestedUser = getUserByUsername("Unknown");
lRequestedUser.getName();
4.) Throwing a custom error
{
...
getUserByUsername(pUserName) {
/* listOfUsers array with objects of the class User */
var lUser = this.listOfUsers.find(user => user.userName === pUserName);
if(_.undefined(lUser)) {
throw new CustomError(...);
}
return lUser;
}
...
}
/* We expect "Unknown" is not a user name in that list*/
try {
var lRequestedUser = getUserByUsername("Unknown");
lRequestedUser.getName();
} catch (e) {
if (e instanceof CustomError) {
//Handling
}
}
Personally I prefer the option to return an object, which contains an indicator, and the option of throwing a custom error. The example is synchronous. In my projects I also have asynchronous code. I’m using promises for that.
So any thoughts that would be the “best” solution or the way I should prefer?