[Switch] Javascript error handling - try...catch

No matter how proficient we are at programming, scripting errors are inevitable. It could be our error, or unusual input, a bad server-side response, and countless other reasons.

Normally, the script stops immediately when an error is sent, printing to the console.

But the try...catchsyntax structure catches errors and can do more than just stop.

Therefore, it is recommended to use Fundebug for online monitoring, and find errors at the first time.  

"try...catch" syntax

try...catchThe structure has two statement blocks, namely try, then catch:

try {

  // code...

} catch (err) {

  // error handling

}

 

The workflow is as follows:

  1. First try{...}the code block executes.
  2. Ignored if there are no errors catch(err): When execution reaches the tryend, skip the catchblock.
  3. If an error occurs, the tryexecution of the block is stopped and the flow of control is entered catch(err). errThe variable (can be any name) contains the information object related to the occurrence of the error.

So, try{...}an error inside the block doesn't stop the script: we have a chance catchto handle it inside the block. Let's see more examples.

  • Error free example: show alert (1) and  (2):

    try {

    alert(‘Start of try runs’); // (1) <–

    // …no errors here

    alert(‘End of try runs’); // (2) <–

    } catch(err) {

    alert(‘Catch is ignored, because there are no errors’); // (3)

    }

    alert(“…Then the execution continues”);

  • Example with error: show  (1)and (3):

    try {

    alert(‘Start of try runs’); // (1) <–

    lalala; // error, variable is not defined!

    alert(‘End of try (never reached)’); // (2)

    } catch(err) {

    alert(Error has occured!); // (3) <–

    }

    alert(“…Then the execution continues”);

try..catch only works on runtime errors

Yes try..catch, the code must be runtime, in other words, must be valid Javascript code. 
If the code syntax error will not work, for example, the following will not catch the curly brace error:

try {
  {{{{{{{{{{{{
} catch(e) {
  alert("The engine can't understand this code, it's invalid");
}

 

The Javascript engine reads the code first, then runs it. Errors that occur during the read phase are called "parse-time" errors and are not recoverable because the engine does not understand the code.

So errors that try...catchcan only be handled in valid code are called "runtime" errors, and sometimes "exceptions".

try..catch executes synchronously

If an exception occurs in scheduled code, e.g. setTimeout, then try...catchdo not catch the exception:

try {
  setTimeout(function() {
    noSuchVariable; // script will die here
  }, 1000);
} catch (e) {
  alert( "won't work" );
}

 

Because it try...catchwraps the setTimeoutcall to the scheduled function. But the function will be delayed execution, at this time the engine has been try...catchstructured immediately.

In order to catch exceptions inside the intended execution function, inside the try...catchfunction:

setTimeout(function() {
  try {
    noSuchVariable; // try..catch handles the error!
  } catch (e) {
    alert( "error is caught here!" );
  }
}, 1000);

 

error object

When an error occurs, Javascript generates an object containing the details and passes it as a parameter to the catchblock:

try {
  // ...
} catch(err) { // <-- the "error object", could use another word instead of err
  // ...
}

 

For all built-in errors, catchthe inner error object has two main properties:

name 
is the wrong name, an undefined variable is “ReferenceError”.

message 
Text description of the error message.

There are other non-standard properties in most environments, one that is widely used and supported is: stack

Current call stack: about the nested call sequence that caused the error, for debugging purposes. 
Example:

try {
  lalala; // error, variable is not defined!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at ...

  // Can also show an error as a whole
  // The error is converted to string as "name: message"
  alert(err); // ReferenceError: lalala is not defined
}

 

Use "try...catch"

Let's explore a real use case:

We know that Javascript supports methods JSON.parse(str)for reading json values. Typically used to parse json data received from the network, such as server-side or other sources. Receive and call JSON.parse, as follows:

let json = '{"name":"John", "age": 30}'; // data from the server

let user = JSON.parse(json); // convert the text representation to JS object

// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age );  // 30

 

If jsonnon-standard, JSON.parse produces an error and the script stops. Will we be satisfied? of course not!

If the data has some kind of error, the user has absolutely no idea what to send (unless the dev console is opened). Nobody likes a script that stops without any error message when an error occurs.

Let's try...catchhandle errors using:

let json = "{ bad json }";

try {

  let user = JSON.parse(json); // <-- when an error occurs...
  alert( user.name ); // doesn't work

} catch (e) {
  // ...the execution jumps here
  alert( "Our apologies, the data has errors, we'll try to request it one more time." );
  alert( e.name );
  alert( e.message );
}

 

Here we use catchblocks to just display information, but can do more: new network requests, suggest an alternative, send error messages to the log, etc., all better than just stopping the code.

throw our own mistakes

What if the json syntax is working, but doesn't have the required name attribute?

as follows:

let json = '{ "age": 30 }'; // incomplete data

try {

  let user = JSON.parse(json); // <-- no errors
  alert( user.name ); // no name!

} catch (e) {
  alert( "doesn't execute" );
}

 

This  JSON.parseworks fine, but the name attribute is missing, which is actually an error for us. To unify error handling, we need to use throwoperations.

Throw operation

This operation produces an error. grammar:

throw <error object>

 

Technically, anything can be used as an error object. Can be a primitive type such as a number or string, but it is better to use an object, with name and message properties (compatible with the built-in error object).

Javascript has many built-in standard error constructors: Error, SyntaxError, ReferenceError, TypeError, and others. We can also use it to create error objects.

grammar:

let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...

 

For built-in error objects (error objects only), the name property is exactly the name of the constructor and message is the constructor parameter.

let error = new Error("Things happen o_O");

alert(error.name); // Error
alert(error.message); // Things happen o_O

 

Let's see JSON.parsethis error generated:

try {
  JSON.parse("{ bad json o_O }");
} catch(e) {
  alert(e.name); // SyntaxError
  alert(e.message); // Unexpected token o in JSON at position 0
}

 

As we can see, the error is: SyntaxError.

In our example, the default name attribute, which can also be considered a syntax error, assumes that users must have a name attribute, so we throw an error:

let json = '{ "age": 30 }'; // incomplete data

try {

  let user = JSON.parse(json); // <-- no errors

  if (!user.name) {
    throw new SyntaxError("Incomplete data: no name"); // (*)
  }

  alert( user.name );

} catch(e) {
  alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}

 

On the asterisked line, the throwoperation produces SyntaxErroran error, with the given message, consistent with the error generated by Javascript itself. Execution in the try block stops immediately and the flow of control jumps to the catchblock.

It's now catcha block that handles all errors independently: JSON.parseand other errors.

throws the error again

In the above example, we use try...catchhandle incorrect data, but it is also possible that other exceptions occur in the try...catchblock like variable undefined or other, not only "incorrect data".

as follows:

let json = '{ "age": 30 }'; // incomplete data

try {
  user = JSON.parse(json); // <-- forgot to put "let" before user

  // ...
} catch(err) {
  alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
  // (not JSON Error actually)
}

 

Of course everything is possible! Errors made by programmers, even by heavily used open source tools - can suddenly find a crazy bug that leads to horrific hacks (as happens with ssh tools).

In our example, this try...catchis to handle incorrect data errors, but in practice, catch catches all errors in the try block. If there is an exception error, the "JSON Error" message is still displayed, such errors also make the code more difficult to debug.

Fortunately, we can find out what type of error is caught, for example, from its name property:

try {
  user = { /*...*/ };
} catch(e) {
  alert(e.name); // "ReferenceError" for accessing an undefined variable
}

 

The rules are simple.

Only known errors should be handled, other errors should be re-throwed. 
The detailed re-throw errors are explained below:

  1. catch all errors
  2. In the catch(err){...}block we analyze the error objecterr
  3. If you don't know how to handle it, then by throw errthrowing an error

In the code below, we use to rethrow the error so that it catchonly handles SyntaxError:

let json = '{ "age": 30 }'; // incomplete data
try {

  let user = JSON.parse(json);

  if (!user.name) {
    throw new SyntaxError("Incomplete data: no name");
  }

  blabla(); // unexpected error

  alert( user.name );

} catch(e) {

  if (e.name == "SyntaxError") {
    alert( "JSON Error: " + e.message );
  } else {
    throw e; // rethrow (*)
  }

}

catchAn asterisk line inside a block throws an error, which can be caught by the outer struct try...catchblock (if it exists), or simply stop the script.

So the catchblock actually only handles known errors and ignores all other errors.

The following example demonstrates that such errors are try...catchhandled by multi-level blocks.

function readData() {
  let json = '{ "age": 30 }';

  try {
    // ...
    blabla(); // error!
  } catch (e) {
    // ...
    if (e.name != 'SyntaxError') {
      throw e; // rethrow (don't know how to deal with it)
    }
  }
}

try {
  readData();
} catch (e) {
  alert( "External catch got: " + e ); // caught it!
}

 

This readDataonly knows how to handle SyntaxError errors, and the external ones try...catchknow how to handle any errors.

try…catch…finally

Wait, it's not over yet.

A structure try...catchcan have multiple code clauses: finally, if present, all cases are executed.

  • tryAfter that, if there is no error condition
  • catchAfter that, if there is an error

The extension syntax looks like this:

try {
   ... try to execute the code ...
} catch(e) {
   ... handle errors ...
} finally {
   ... execute always ...
}

 

Please try running the code below:

try {
  alert( 'try' );
  if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
  alert( 'catch' );
} finally {
  alert( 'finally' );
}

 

The code has two execution paths:

  1. If answering "Yes" produces an error, the execution path is try->catch->finally.
  2. If the callback is "No", then the path is try->finally.

Clauses finallyare usually used when try...catchyou start doing something before a block and need to terminate regardless of the result.

For example, we want to measure the fib(n)execution time of the Fibonacci function. Naturally, we need to measure before and after execution. But what if there is an error during the function call? In particular, the implementation of fib(n) in the code below will return an error for negative or non-integer numbers.

No matter what happens, clauses finallyare great for completing time measurements.

finallyResponsible for testing execution time in two scenarios - successful fibfunction execution and error conditions:

let num = +prompt("Enter a positive integer number?", 35)

let diff, result;

function fib(n) {
  if (n < 0 || Math.trunc(n) != n) {
    throw new Error("Must not be negative, and also an integer.");
  }
  return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}

let start = Date.now();

try {
  result = fib(num);
} catch (e) {
  result = 0;
} finally {
  diff = Date.now() - start;
}

alert(result || "error occured");

alert( `execution took ${diff}ms` );

 

You can enter 35 according to the prompt to check that the code is running - the execution is normal, and then the try is executed finally. Enter -1 again, an error occurs immediately, the execution takes 0ms, and the time measurement is completed correctly.

In other words, there are two ways to exit a function: either return, or throw an error. finally subhandles are processed.

Variables in try...catch...finally blocks are local variables

Note that in the above code resultand diffvariables, they are try...catchdeclared before the block.

Otherwise, if you use let inside a {...} block, it will only be visible inside the block.

finally and return

The finally clause is executed no matter how the try...catchblock ends. Including the displayed return method to return.

The following example, with return in try, in this case finally is executed before control returns to the outer code.

function func() {

  try {
    return 1;

  } catch (e) {
    /* ... */
  } finally {
    alert( 'finally' );
  }
}

alert( func() ); // first works alert from finally, and then this one

 

try...finally 
try...finally constructs, without catchclauses, are also useful. Applies when we don't want to handle errors here, but make sure the start and end processes are executed.

function func() {
  // start doing something that needs completion (like measurements)
  try {
    // ...
  } finally {
    // complete that thing even if all dies
  }
}

 

In the above code, the error of the try block will always happen because there is no catch block, but the finally will execute before the execution flow jumps outside.

global error catch

The information in this section of the environment specification 
is not part of core JavaScript.

We imagine try...catchthat there is a fatal error outside the block, and the code stops immediately. Like a programming error or something worse.

Is there a way to deal with such events? We might want to log an error, show some information to the user (usually he won't see the error message), etc.

Not covered in the Javascript spec, but environments usually provide implementations because they do. For example, Node.JS has process.on("uncaughtException"), and you can assign a function to window.onerror in the browser. It will run without catching errors.

grammar:

window.onerror = function(message, url, line, col, error) {
  // ...
};

message 
error message

url 
error script url

line, col

The error occurs in the number of lines, columns in the code

error 
error object

<script>
  window.onerror = function(message, url, line, col, error) {
    alert(`${message}\n At ${line}:${col} of ${url}`);
  };

  function readData() {
    badFunc(); // Whoops, something went wrong!
  }

  readData();
</script>

The role of global error handling window.errorgenerally cannot resume script execution, which is not possible in the case of programming errors, but will send error messages to the developer.

There are also web services that provide error logging for this situation, such as  https://errorception.com  or  http://www.muscula.com .

The workflow is as follows:

  1. Register the service, and then get a JS script to insert into the page.
  2. There are custom window.errorfunctions in the JS script.
  3. When an error occurs, a network request is sent to the server.
  4. We can log into the web interface of the service to view the error message.

Summarize

Structs try...catchcan handle runtime errors, literally trying to run the code and then catching errors that might occur.

The syntax is:

try {
  // run this code
} catch(err) {
  // if an error happened, then jump here
  // err is the error object
} finally {
  // do in any case after try/catch
}

There may also be no catchor finallyblock, so try...catchand try...finallyare both valid syntax.

Error objects have the following properties:

  • message- User understandable error messages.
  • name-- Error name string (error constructor name).
  • stack--Non-standard --The stack message where the error occurred.

We can also throwgenerate our own errors by using operations, which technically throwcan be of any type, but are usually Errorobjects inherited from the built-in error classes. Extended error objects are described later.

Rethrowing is the basic pattern of error handling: catcha block usually expects and knows how to handle a particular error, so it should rethrow unknown errors.

Even if we don't use try...catchit, most environments support setting global error handling to catch all errors that occur, the browser's built-in window.onerror.

 

Original text: https://blog.csdn.net/neweastsun/article/details/76358623

 

you may be interested in

  1. Detailed explanation of 10 major JavaScript errors from 1000+ project data analysis
  2. Prompt "username or password is incorrect" is bad
  3. Debug front-end HTML/CSS
  4. Where there is a browser, there is Fundebug

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325047146&siteId=291194637