Control flow: try-catch or if-else?
Juhee Kang
Posted on April 6, 2021
Introduction
Lately, while working on a new project, I had a chance to think about the proper way to handle control flow. As all developers might know well, the most common approaches used are try-catch
and if-else
. So far, I haven't thought deeply about the difference between these two. From time to time, I think I mainly adopted a more concise way of writing code. Stability was significant on this project, applying the appropriate control flow processing approach was one of the keys. For this reason, I wrote pseudocode based on the scenario for these two approaches and compared them, and I'd like to share the result with this community.
Scenario
Suppose that the signup logic of the virtual Web service 'A' is not allowed to have duplicate nicknames, e-mails, and phones.
The signup verification process is as follows:
1. Check for duplicate nickname.
2. Check duplicate on personal information.
a. Verify email address is unique.
b. Verify phone number is unique.
The code below is an implementation of this logic.(I've excluded error-handling logic at here on purpose.)
// signUp.js
function signUp(){ // function of signup
userTable.isUnique(nickname) // Check for duplicate nickname.
duplicateDetailInfo(email, phone); // Check duplicate on personal information.
addUser(user) // Add a user
}
// duplicateDetailInfo.js
function duplicateDetailInfo(email, phone){
userTable.isUnique(email); // Verify email address is unique.
userTable.isUnique(phone); // Verify phone number is unique.
}
// userTable.js
class userTable {
function isUnique(value){
// Will be implemented according to each approach
}
}
Although it's not perfect, I purposely wrote the code to explain the difference in control flow.
On this code, depending on how to implement userTable.isUnique
as Exception or return values(false, null, etc..), I will explain using try-catch
and if-else
, respectively.
try-catch
Exception
On this implementation approach, userTable.isUnique()
will raise an error if the value exists.
// signup.js
function signUp(){
try {
userTable.isUnique(nickname); // Raise Exception if the nickname is not unique.
duplicateDetailInfo(email, phone); // Check for duplicate personal information.
} catch (e) {
console.log("fail")
}
addUser(user);
}
// duplicateDetailInfo.js
function duplicateDetailInfo(email, phone){
userTable.isUnique(email); // Raise Exception if the email is not unique.
userTable.isUnique(phone); // Raise Exception if the phone is not unique.
}
// userTable.js
class userTable {
function isUnique(value){
value = userDB.find(value);
return !value? true: throw Error(); // Raise Exception if the value is not unique.
}
}
The problem with this approach is that the flow of processing is not explicitly visible.
signUp()
├── try
│ ├── .isUnique(nickname)
│ │ └── raise Exception
│ │
│ └── duplicateDetailInfo()
│ ├── .isUnique(email)
│ │ └── raise Exception
│ │
│ └── .isUnique(phone)
│ └── raise Exception
│
└── catch
For example, the nickname exception is processed by the parent function (signUp), so you can easily find the order of the control flow. However, for email and phone exceptions, it is difficult to identify the control flow because it is not easy to know where to handle the exceptions.
This code consists of two depths, so it is easy to verify, but it is not known what happens to the control flow after this line code. In case when try-catch
is used multiple times, it will become harder to figure out the control flow. Furthermore, code will be less intuitive.
Of course, this try-catch
approach has the advantage of being able to handle all exceptions in one place. But, this can be a disadvantage. However, if there are hundreds of exceptions, the code may also be less intuitive because different exception logic is handled in a single location.
if - else
On this implementation approach, userTable.isUnique()
will return true if the value exists.
// signup.js
function signUp(){
if (!userTable.isUnique(nickname)) { // Return false if the nickname is not unique.
return console.log("fail")
}
if(!duplicateDetailInfo(email, phone)) { // Return false if the details is not unique.
return console.log("fail")
};
addUser(user);
}
// duplicateDetailInfo.js
function duplicateDetailInfo(email, phone){
if(!userTable.isUnique(email)) { // Return false if the email is duplicated.
return false;
}
if(userTable.isUnique(phone)) { // Return false if the phone is duplicated.
return false;
};
return true
}
// userTable.js
class userTable {
function isUnique(value){
value = userDB.find(value);
return value? true: false; // Return false if the value is not unique.
}
}
For this approach, predictable and formatting can safely implement code is an advantage.
The advantage of this approach is that it can predict the flow of code and implement the code by specifying the return type(in this case, boolean). Since try-catch
statement is not used, the control flow can be easily to figure out because it is handlded directly from a parent function rather than from another location(exception catch). By this benefit, it is usally possible to check quickly even if something goes wrong.
signUp()
├── .isUnique(nickname)
│ └── return false? => handling error
│
└── duplicateDetailInfo()
└── return false? => handling error
In the previous try-catch
case shown earlier, it is difficult to identify control flow because it was not easy to determine where exceptions is handled in the case of overlapping email and phone. On the other hand, for the if-else
approach, the control flow is intuitively processed according to the return value of function, so it is easy to find out what logic runs next and where the error occurs.
Of course, this approach also has the disadvantage of having to branch out with if statements for every case that occurs.
Conclusion
I looked up a lot of materials about control flow and found that using try-catch
was considered an anti-pattern because it is difficult to identify the flow of code. Therefore, I believe that the best way is to treat the control flow as an intuitive if-else
approach according to the return value, even if the amount of code is large, rather than a try-catch
approach where the code is concise but unclear where an exception is handled.
If you want to get further details about why try-catch
exception handling is an anti-pattern, I recommend you refer to this post.
This article is originally published by myself here.
Posted on April 6, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.