Control flow: try-catch or if-else?
Tags: performance, javascript, tutorial, webdev
Published: 2021-04-06 12:03:01

## 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.)

```js
// 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.***

```js
// 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.

```bash 
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.***

```js
// 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.

```bash 
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](https://softwareengineering.stackexchange.com/q/189222).

This article is originally published by myself [here](https://dzone.com/articles/control-flow-try-catch-or-if-else).