DEV Community

Cover image for Control flow: try-catch or if-else?
Juhee Kang
Juhee Kang

Posted on • Edited on

Control flow: try-catch or if-else?

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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.isUniqueas 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.
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)