preface

  • Code execution exception is we often encounter, unexpected situations always emerge in an endless stream, thrown errors, and sometimes even lead to a white screen, encountered exceptions ignored is obviously not a qualified programmer should do.

  • Exceptions occur every year, all the time, but what matters more is how we handle them.

  • If we can prepare for mistakes in advance, respond to mistakes captured and give feedback to users, from the perspective of user experience, it can reduce users’ dissatisfaction with our products to a certain extent.

  • User-facing, more user-friendly applications have a higher urgency and need for exceptionally comprehensive and correct processing.

  • Of course, the best thing to do is to make sure your code doesn’t break as many exceptions as possible.

As for the importance of exception handling, take the most common example: entering a page if our code execution fails, the program will be blocked and the page will display a white screen. But if we show an error page for a system exception, it might be slightly better for the user’s experience.

We’ve probably all had these problems, right?

  • When to catch a try-catch and when to throw an exception to the upper layer.

  • How to handle an exception after it is thrown. How do I return page error messages

  • Do you need to catch and handle possible exceptions in all cases? When can exceptions be ignored?

  • .

Let’s take a look at some of the basics of exceptions and then explore them for business scenarios:

What is an anomaly?

Exceptions are unexpected events that often affect the correct operation of a program.

In essence, an exception is an object that stores information about the occurrence of the exception, such as error codes, error stacks, and other error information.

1.1 Relationship between errors and exceptions

It is important to note that errors only generate exceptions if they are thrown, and errors that are not thrown do not.

function test() { console.log("test start"); new Error(); console.log("test end"); } test(); // Output: // test start // test end // No exception will be thrownCopy the code

The above code does not throw an exception after execution.

2. Classification of anomalies

Exceptions can be divided into two categories: run-time and compile-time.

  • Compile-time exceptions are exceptions generated by source code before it is translated into executable code.

  • Runtime exceptions are exceptions that occur after executable code is loaded into memory for execution.

2.1 Exceptions during compilation

const s: string = 10001;
Copy the code

Typescript compiles into JS code that is executed at the JS Runtime. In the example above, TS raises a compilation exception while compiling. (Type ‘10001’ is not assignable to Type ‘string’.)

Although JS is an interpretive language, there are still compilation links. The code is still lexed and parsed before execution.

Function test() {console.log("before") await fetch console.log("end")} test() // Uncaught SyntaxError: Await is only valid in async functions and the top level bodies of modules async function test() {// syntax error Log ("before") console.log("end")} test() // Uncaught SyntaxError: Unexpected token 'function'Copy the code

Compile-time exceptions are discovered as we compile, so they’re not very dangerous to us, but really the most dangerous ones are runtime exceptions, which are also the most common ones.

2.2 Runtime Exceptions

function a() { console.log('before'); Throw new Error(' Running... '); console.log("after"); } a(); // Before // Uncaught Error: Running...Copy the code

From the above example, we can get a sense of its nature as a runtime exception: thrown during code execution.

Third, the spread of anomalies

We’re all familiar with browser event propagation, and exceptions propagate similarly.

  • Exception propagation is applied to the function call stack.

  • There is no catch mechanism for exception propagation.

  • JS exception passing is automatic, you do not need to manually pass layer by layer.

  • If an exception is not caught, it is passed down the call stack until the stack is empty.

  • Propagation begins when an exception is thrown.

  • If an exception is not caught, it is eventually printed in the console. The error type is UncaughtError.

  • If an exception is not caught, it is eventually printed in the console. The error type is UncaughtError.

  • In actual projects, such UncaughtError exceptions are the easiest to detect, so try not to make your system immune to such exceptions.

The main purpose of Guard is to catch global exceptions that are not handled by a catch and report them to the developer.

4. How exceptions are thrown

4.1 Manual Throwing

throw new Error(`error message`);
Copy the code

4.2 Automatic Throwing

const obj = null;
obj.toString();
Copy the code

Question: When should you throw an exception manually? [Principle 1] : When you have a hunch that your program will not perform correctly, you can throw an error. [Case] : When you provide a general service and need the user to pass in an option, we usually parse the option. For unreasonable input, we usually need to throw an exception with clear meaning to the user (custom exception), telling it that we cannot handle it.

Processing of input that does not conform to the specification can be represented by a different return value in addition to an exception thrown.

function test(a, b) {
  // ...
  if (!b) {
    return new Error({
      code: 1,
      message: "Invalid params " + b,
    });
  }

  if (Number.isNaN(b)) {
    return new Error({
      code: 2,
      message: "Invalid params" + b,
    });
  }
  // ....
  return ;
}

const res = test(2, "1");
if (res.code === 1) {
  return console.log("error1");
}
if (res.code === 2) {
  return console.log("error2");
}
Copy the code

[Principle 2] : If an exception has been caught, do not want to block its propagation

【 Case 】 :

async pageInit() { try { await this.fetchData(); await this.calculateValue(); / /... Other methods this.updateStatus(types.pagestatus.READY); } catch (error) { console.error('failed to pageInit', error); This.resolveerror (error); this.resolveError(error); } } fetchData() { try { // .... } catch(e) {// Do part of the processing for this exception // eg: toast prompt throw e; }}Copy the code

5. Exception handling & Case analysis

5.1 Try Catch Review

For exception catching and handling, we use try catch. We should be familiar with it. Here are a few key points to review and improve our understanding of it.

  • Try catch handles runtime exceptions

Try catch code must be syntactically free, otherwise a try catch will not work. And it handles runtime exceptions.

Try {{{{{{{{{{{{{/ / grammar abnormal} the catch (e) {the console. The log (" can 't understand this code "); } try {test(); } catch(err) { alert(`Error has occurred! `); } function test() { console.log('Start'); balabala(); // Error, undefined! console.log('End'); } // Start // Error has occurred!Copy the code
  • Try catch only supports synchronization

A try catch can only handle synchronous code. Asynchronous errors cannot be caught with a try catch, but rather with a callback. The reason is that a try catch simply catches errors in the current call stack.

Try {setTimeout(function() {throw new Error(' Error ') // This Error will not be caught}, 2000); } catch (e) { console.log( "error" ); // never execute}Copy the code
  • Try catch allows exception code to continue execution

Try catch allows exception code to continue execution without blocking program execution.

function pageInit() { fetchData(); console.log('pageInit end') } function fetchData() { getBaseInfo(); console.log('fetchData end') } function getBaseInfo() { try { console.log('getBaseInfo'); throw new Error('error'); } catch(e) {console.log(' catch exception ', e)}} pageInit(); // getBaseInfo // catch Error: error // at getBaseInfo (<anonymous>:14:11) // at fetchData (<anonymous>:7:3) // at pageInit (<anonymous>:2:3) // at <anonymous>:20:1 // fetchData end // pageInit endCopy the code

What about not capturing? An exception thrown without a catch directly blocks the execution of the program.

function pageInit() {
  fetchData();
  console.log('pageInit end')
}

function fetchData() {
  getBaseInfo();
  console.log('fetchData end')
}

function getBaseInfo() {
    console.log('getBaseInfo');
    throw new Error('error');
}

pageInit();
// getBaseInfo
// Uncaught Error: error
//    at getBaseInfo (<anonymous>:14:11)
//    at fetchData (<anonymous>:7:3)
//    at pageInit (<anonymous>:2:3)
//    at <anonymous>:20:1
Copy the code

5.2 Exception handling when the call stack is deep

Take the following case to illustrate:

function pageInit() {
  fetchData();
  console.log('pageInit end')
}

function fetchData() {
  getBaseInfo();
  console.log('fetchData end')
}

function getBaseInfo() {
    console.log('getBaseInfo');
    throw new Error('getBaseInfo  error');
}

pageInit();
Copy the code

To catch getBaseInfo:

function pageInit() {
  fetchData();
  console.log('pageInit end')
}

function fetchData() {
  getBaseInfo();
  console.log('fetchData end')
}

function getBaseInfo() {
    try {
        console.log('getBaseInfo');
        throw new Error('getBaseInfo  error');
    } catch(e) {
        console.log('getBaseInfo error', e);
    }
}

pageInit();
Copy the code

Above code execution: The program executes normally and is not blocked by exceptions thrown midway.

There’s a fatal flaw here, but fortunately we also print a log. console.log(‘getBaseInfo error’, e); Without this log, any errors in the getBaseInfo execution are invisible to the developer. From the beginning of development to the final launch, there may be hidden bugs that we haven’t noticed.

function getBaseInfo() { try { console.log('getBaseInfo'); throw new Error('getBaseInfo error'); } catch(e) {this.loadedBaseInfo = false; / /... }}Copy the code

If you find a getBaseInfo execution that might be wrong, we can’t see anything on the console without a catch and no output.

The code above does not throw any exceptions, it is completely engulfed, which is a disaster for us to debug the problem. So don’t swallow exceptions that you can’t handle. Don’t be afraid of exceptions. Throwing exceptions makes it easier for us to solve them. The “right way” : Catch exceptions you can handle and throw exceptions you can’t handle.

Rewrite the above example:

function pageInit() { try { fetchData(); this.pageStatus = 'ready'; console.log('pageInit end') } catch(e) { console.error('failed to pageInit', e); this.resolveError(e); }} function fetchData() {try {getBaseInfo(); / /... Log ('fetchData end')} catch(e) {console.error('failed to fetchData', e); Toast (' failed to initialize page data '); throw e; } } function getBaseInfo() { try { console.log('getBaseInfo'); throw new Error('getBaseInfo error'); } catch(e) { console.error('failed to getBaseInfo', e); Toast (' failed to get basic information '); throw e; } } pageInit();Copy the code

[QUESTION] : Do nested functions always have to try catch? Obviously not, it depends on the actual business scenario. But when you do a catch, and you do something about the exception that you caught. When you’re not sure if you’ve done everything right, our best option is still to throw the error out again.

[QUESTION] : Is there any other consideration for the rewriting of the appeal case? Yes, execute fetchData and getBaseInfo, both of which have specific tasks, and each method alone knows what it is doing and what it is trying to do. Obviously, they are also the most aware of their own possible anomalies. Executing your own try catch in each of these methods is most appropriate for your own exception. If the exception is handled in pageInit, then it is more troublesome to give more specific hints and handling for different errors.

5.3 Other Case 1

async created() { await this.initState(); await this.fetchData(); }, methods: FetchData () {fetchData() {fetchData() {fetchData() {fetchData() {fetchData() {fetchData() {fetchData() {fetchData(); Update state} catch(err) {this.pageStatus = 'error'; }}}Copy the code

The above example is obviously not reasonable.

There must be an initPage after the page enters, and then we must try catch, so please try catch everything that you initialized. We only try catch some operations, so it’s not very strict.

5.4 Other Case 2

function getBaseInfo() { try { console.log('getBaseInfo'); throw new Error('getBaseInfo error'); } catch(e) { console.error('failed to getBaseInfo', e); Toast (' failed to get basic information '); If (isInnerError(error)) {if (error.code === '-1') {toast(' network exception, Please try again later ')} else if (error.code === '-3') {toast(' system exception, please try again later ')}} else {// Other exceptions, possibly JS runtime exception toast(' system execution exception ') throw e; }}}Copy the code

Once an exception is caught, handle it with care. For internally recognized error types, we can handle them directly, but for unknown exceptions, we must throw them.

This prevents the exception from being exposed after a try catch, and the global exception capture mechanism such as Guard cannot obtain the exception.

5.5 Other Cases 3

Async updateRewardConfig(rewardConfigList) {try {const res = await BindingAPi.updateRewardConfig(rewardConfigList); If (res) {this.showtoast ('error', 'update reward configuration successful ') return true; } return false; } catch (e) { console.error('failed to updateRewardConfig', e, rewardConfigList); This. ShowToast ('error', 'update reward configuration failed, please try again ') throw e; } } const result = this.updateRewardConfig(rewardConfigList); If (result) {this.$router.go(-1); }Copy the code

Depends on the return value of a method that has a try catch inside it. If the caught exception is not thrown, subsequent code execution cannot be blocked in the event of an error.

Conclusion: For exception handling, it is impossible to list all the cases, let’s form a dead rule, to apply.

The most important thing is that we should master a set of exception handling principles suitable for ourselves, constantly reflect and summarize ourselves in daily business scenarios, and finally form a set of habits and methods of our own.

Making your code more robust is the most effective way to reduce exceptions. Using catch correctly and adding a global exception catching mechanism can help us find problems in our code earlier and resolve them sooner. Give yourself more confidence in your project.

chattering

I’m going to share a couple of things that I think we can think about over and over when we’re writing code.

  • As much as possible to improve the robustness of our code, to develop their own coding literacy.

  • It’s good to be a “neat freak” when it comes to writing code. Think of your project as your work, and you should be aware of the entire execution process and where it might be flawed.

  • Think about it before you start coding. What are you thinking? (E.g. : What do I want to implement? How about this? Is there a better way to write it or a more reasonable way to write it?