Recently in the project upgrade, the new interface has different error codes to return to various forms of error processing. However, the error handling of the old project was not perfect. After the result was completed, the leader said when the code was reviewed: the code was unreadable… Take a hard look at what good error handling looks like.

In terms of the title of the best is just a personal currently feel more appropriate error processing, yes is the title party, see the officer grandpa keep calm, do not spray for let go.

Exceptions are usually found in interface requests, of course exceptions can occur in any operation, but most of them were solved during development. Once an exception occurs we may need to deal with various types of error codes. The first best practice is unified error code management

Suppose you have two requests, getCookies and getCakes, both of which are requested via the getDessert interface, but which require different error messages depending on the error code.

Concentrate errors

If we spread out errors, we run the risk of incomplete error handling and the introduction of all kinds of strange things.

Cookies and cakes are handled centrally in this code, but there’s still a problem with the following code that I’ll talk about in the next point.

For the error code in the project, it is best to maintain it uniformly by one file and one method, so that the error can be handled uniformly, and it is much easier to process the returned value twice in the later period.

const COOKIE_OUT_OF_STOCK = 'COOKIE_OUT_OF_STOCK';
const NO_CAKE = 'NO_CAKE';
const COOKIE = 'COOKIE';
const CAKE = 'CAKE';

class Dessert{
    getDessert(type) {
        if(type === COOKIE){
            return COOKIE_OUT_OF_STOCK;
        } else if(type === CAKE){
            return NO_CAKE;
        }
    }
}

const dessert = new Dessert();
const errorCake = dessert.getDessert(CAKE);
if(errorCake === NO_CAKE){
    console.log('cake out of stoke');
}
if(errorCake === COOKIE_OUT_OF_STOCK) {
    console.log('cookie out of stoke');
}
Copy the code

Throw an error instead of returning an error code

The above code uses a return error code for error handling, but this method is loud and inelegant. Using an error-throwing method like a try catch makes the code look cleaner and makes it clear that error handling is taking place.

const COOKIE_OUT_OF_STOCK = 'COOKIE_OUT_OF_STOCK';
const NO_CAKE = 'NO_CAKE';
const COOKIE = 'COOKIE';
const CAKE = 'CAKE';

class DESSERT{
    getDessert(type) {
        if(type === COOKIE){
            throw new  Error(COOKIE_OUT_OF_STOCK);
        } else if(type === CAKE){
            throw new  Error(NO_CAKE);
        }
    }
}

const dessert = new Dessert();

try{
    const errorCake = dessert.getDessert(CAKE);
} catch(e) {
    console.log(e)
}
Copy the code

Instead of iterating through all the error codes in the business code, we put the code we need to process into a try and throw the corresponding error directly through a catch.

The code execution after the try

try{
    const errorCake = dessert.getDessert(CAKE);
    console.log('Shop is closed');
} catch(e) {
    console.log(e);
}
Copy the code

If we had any other logic in the try after the error, that part of the logic would not be executed.

finally

However, if we want to execute later code, such as destroying the instance or stopping the timer, we may need to use finally.

try{
    const errorCake = dessert.getDessert(CAKE);
} catch(e) {
    console.log(e);
} finally{
    console.log('Shop is closed');
}
Copy the code

If the code in a try is synchronized, this is the case.

Error handling of asynchronous code

If an asynchronous request is used within the try block, the code after the promise will still be executed.

const COOKIE_OUT_OF_STOCK = 'COOKIE_OUT_OF_STOCK';
const NO_CAKE = 'NO_CAKE';
const COOKIE = 'COOKIE';
const CAKE = 'CAKE';

class Dessert{
    getDessert(type) {
        if(type === COOKIE){
            Promise.reject( COOKIE_OUT_OF_STOCK);
        } else if(type === CAKE){
            Promise.reject( NO_CAKE);
        }
    }
}

const dessert = new Dessert();

try{
     const errorCake = dessert.getDessert(CAKE);
    console.log('error')
} catch(e) {
    console.log(e)
}
Copy the code

If you want to catch an errorCake using a catch, you use the catch method of a Promise, and then comes after the catch.

const COOKIE_OUT_OF_STOCK = 'COOKIE_OUT_OF_STOCK';
const NO_CAKE = 'NO_CAKE';
const COOKIE = 'COOKIE';
const CAKE = 'CAKE';

class Dessert{
    getDessert(type) {
        if(type === COOKIE){
           return Promise.reject( COOKIE_OUT_OF_STOCK);
        } else if(type === CAKE){
           return Promise.reject( NO_CAKE);
        }
    }
}

const dessert = new Dessert();


dessert.getDessert(CAKE). catch(e => {
    console.log(e)
}).then(() => {
     console.log('error')});Copy the code

Async/await error thrown

But the chain-call method is still a bit different from our original code. What if we use async/ await instead of promise then?

const COOKIE_OUT_OF_STOCK = 'COOKIE_OUT_OF_STOCK';
const NO_CAKE = 'NO_CAKE';
const COOKIE = 'COOKIE';
const CAKE = 'CAKE';

class Dessert{
    getDessert(type) {
        if(type === COOKIE){
           return Promise.reject( COOKIE_OUT_OF_STOCK);
        } else if(type === CAKE){
           return Promise.reject( NO_CAKE);
        }
    }
}

const dessert = new Dessert();

try{
 await dessert.getDessert(CAKE);
 console.log('closed');

} catch(e){
console.log(e)
}
Copy the code

At this point, we were surprised to find that Closed did not execute and instead threw an error.

So use finally again

try{
 await dessert.getDessert(CAKE);
} catch(e){
 console.log(e)
} finally{
  console.log('closed');
}
Copy the code

After catching an error

In the example above, all errors are output using console.log, which is not obvious from the console.

After catching an error, we want to have an obvious output to point out the error, rather than just having it thrown and gone. If there is no required error handling in a catch, it is recommended to use console.error for error throwing.

In addition, I don’t recommend putting special handling of errors in (this would make the entire catch block bloated and uncompact. Add an intermediate layer between the interface layer and the business layer to process the data and errors in bulk. (Yes, that’s a summary of being rubbed.)

In addition, it is best not to use the default throw. A good throw is to tell exactly what happened, preferably in a full sentence or something that is easy to understand.

conclusion

Try,catch makes error handling a little cleaner (as opposed to handling various error cases directly). Despite the nuggets’ previous try catch debunks, error handling in the business is necessary. There are some try catch issues, probably due to a lack of understanding of the use of try catches (such as error reprocessing after a catch). This is not to say that try catches are perfect, but error handling should be a part of a project, even an important one.

If there is anything inappropriate in this article, please point it out in the comments section.