[Turn] JavaScript exception handling

When the  JavaScript  engine executes  JavaScript  code, various exceptions may occur, such as syntax exceptions, missing features in the language, exceptions due to abnormal output from the server or the user.

The  Javascript  engine is single-threaded, so once an exception is encountered, the Javascript  engine usually stops execution, blocks subsequent code and throws an exception message. Therefore, for foreseeable exceptions, we should capture and correctly display them to users or developers.

Error object

throw  and  Promise.reject()  can throw exceptions of type string, and they can throw  exceptions of type Error  object.

An  exception of type Error  object not only contains an exception information, but also contains a traceback stack so that you can easily find the number of lines of code that went wrong through the traceback stack.

Therefore, it is recommended to throw  exceptions of type Error  object, rather than exceptions of type string.

Create your own exception constructor

function MyError(message) {
    var instance = new Error(message); instance.name = 'MyError'; Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); return instance; } MyError.prototype = Object.create(Error.prototype, { constructor: { value: MyError, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf) { Object.setPrototypeOf(MyError, Error); } else { MyError.__proto__ = Error; } export default MyError;

Throw and catch custom exception types in your code

try {
    throw new MyError("some message");
} catch(e){ console.log(e.name + ":" + e.message); }

Throw

throw expression;

The throw  statement is used to throw a user-defined exception. Execution of the current function will be stopped ( statements after throw  will not be executed), and control will be passed to the first  catch  block in the call stack. If there is no catch block in the caller function   , the program will terminate.

try {
    console.log('before throw error');
    throw new Error('throw error'); console.log('after throw error'); } catch (err) { console.log(err.message); } // before throw error // throw error

Try / Catch

try {
   try_statements
}
[catch (exception) {
   catch_statements }] [finally { finally_statements }]

try/catch  is mainly used to catch exceptions. A try/catch  statement consists of a  try  block, and at least one  catch  block or one  finally  block. There are three forms of  try statements:

  • try...catch
  • try...finally
  • try...catch...finally

 Put statements or functions that may generate exceptions in try blocks

The catch  block contains the statement to be executed. When   an exception is thrown in the try block, the catch  block will capture the exception information and execute   the code in the  catch block. If  no exception is thrown  in the try block, the catch  block will jump over.

The finally  block is executed after the  try  block and  catch  block. It always executes regardless of whether an exception is thrown or caught. When the exception information is thrown in the  finally block, the exception information in the try block is  overwritten   .

try {
    try {
        throw new Error('can not find it1'); } finally { throw new Error('can not find it2'); } } catch (err) { console.log(err.message); } // can not find it2

If you return a value from  a finally  block, that value will be the return value of the entire  try-catch-finally  , regardless of whether there are  return  statements in  try  and  catch  . This includes   exceptions thrown in catch blocks.

function test() {
    try { throw new Error('can not find it1'); return 1; } catch (err) { throw new Error('can not find it2'); return 2; } finally { return 3; } } console.log(test()); // 3

Try / Catch performance

A well-known anti-optimization pattern is the use of  try/catch .

The use of try/catch statements in V8 (other JS engines may also happen) functions can   not be optimized by the V8 compiler. Reference http://www.html5rocks.com/en/tutorials/speed/v8/

window.onerror

By defining an event listener function on  window.onerror  , uncaught exceptions generated by other code in the program are often caught by   the listener function registered on window.onerror . And at the same time capture some information about the exception.

window.onerror = function (message, source, lineno, colno, error) { }
  • message: exception information (string)
  • source: The script URL where the exception occurred (string)
  • lineno: The line number (number) where the exception occurred
  • colno: The column number (number) where the exception occurred
  • error: Error object (object)

Note: Safari and IE10 do not yet support the   use of a fifth parameter  in the window.onerror callback function, which is an Error  object with a traceback stack

try/catch  cannot catch exceptions in asynchronous code, but it will throw the exception globally and  window.onerror  can catch it.

try {
    setTimeout(() => {
        throw new Error("some message"); }, 0); } catch (err) { console.log(err); } // Uncaught Error: some message
window.onerror = (msg, url, line, col, err) => {
    console.log(err);
}
setTimeout(() => { throw new Error("some message"); }, 0); // Error: some message

In Chrome, window.onerror  can detect exceptions in script files referenced from other domains and flag these exceptions as Script error. If you don't want to deal with these script files imported from other domains, you can Script errorfilter them out by tagging in the program. However, in Firefox, Safari or IE11, cross-domain JS exceptions are not introduced, and even in Chrome, if you surround these nasty code with  try/catch  , then Chrome will no longer detect these cross-domain exceptions.

In Chrome, if you want   to get complete cross-origin exception information through window.onerror , these cross-origin resources must provide appropriate cross-origin header information.

Exceptions in Promises

Exception thrown in Promise

new Promise((resolve,reject)=>{
    reject();
})
Promise.resolve().then((resolve,reject)=>{
    reject();
});
Promise.reject();
throw expression;

Catching exceptions in promises

promiseObj.then(undefined, (err)=>{
    catch_statements
});
promiseObj.catch((exception)=>{
    catch_statements
})

In a  JavaScript  function, only  return  /  yield  /  throw  will interrupt the execution of the function, nothing else can prevent it from running to the end.

 Adding  return before  resolve  /  reject  prevents further execution.

without return

Promise.resolve()
.then(() => {
    console.log('before excute reject'); reject(new Error('throw error')); console.log('after excute reject'); }) .catch((err) => { console.log(err.message); }); // before excute reject // throw error // after excute reject

use return

Promise.resolve()
.then(() => {
    console.log('before excute reject'); return reject(new Error('throw error')); console.log('after excute reject'); }) .catch((err) => { console.log(err.message); }); // before excute reject // throw error

Throw or Reject

 "Synchronous" exceptions are caught by both  try/catch  and  promises

reject  is a callback, and  throw  is just a synchronous statement, if thrown in another asynchronous context, it cannot be caught in the current context.

So   using  reject in Promise  throws an exception. Otherwise  , the catch  may not be caught.

Promise.resolve()
.then(() => {
    setTimeout(()=>{ throw new Error('throw error'); },0); }) .catch((err) => { console.log(err); }); // Uncaught Error: throw error
Promise.resolve()
.then(() => {
    return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('throw error')); }, 0); }); }) .catch((err) => { console.log(err); }); // Error: throw error

window.onunhandledrejection

window.onunhandledrejection Similarly  , fired when window.onerror a JavaScript Promise is rejected  but  there is no  catch  to catch the  rejection . And at the same time capture some information about the exception.

window.onunhandledrejection = event => { 
    console.log(event.reason);
}

eventAn event is  an instance of PromiseRejectionEvent  , which has two properties:

  • event.promise: JavaScript Promise that was rejected
  • event.reason: A value or Object indicating why the promise was rejected, as in  Promise.reject()  .

window.rejectionhandled

Because  Promise  can call  the catch  method later, if the  catch method  is not called to  catch when reject is thrown, but the catch is called  again later  , the rejectionhandled event will be triggered   .

window.onrejectionhandled = event =>
{
    console.log('rejection handled'); } let p = Promise.reject(new Error('throw error')); setTimeout(()=>{ p.catch(e=>{console.log(e)}); },1000); // Uncaught (in promise) Error: throw error // 1秒后输出 // Error: throw error // rejection handled

Unified exception handling

The exception thrown in the code, one is to be displayed to the user, the other is to be displayed to the developer.

For exceptions displayed to users,  alert  or  toast are generally used  ; for exceptions displayed to developers, they are generally output to the console.

In a function or a code block, the thrown exceptions can be captured uniformly and displayed in different ways according to different exception types.

The type of exception that needs to be clicked to confirm:
ensureError.js

function EnsureError(message = 'Default Message') { this.name = 'EnsureError'; this.message = message; this.stack = (new Error()).stack; } EnsureError.prototype = Object.create(Error.prototype); EnsureError.prototype.constructor = EnsureError; export default EnsureError;

Exception type of pop-up prompt:
toastError.js

function ToastError(message = 'Default Message') { this.name = 'ToastError'; this.message = message; this.stack = (new Error()).stack; } ToastError.prototype = Object.create(Error.prototype); ToastError.prototype.constructor = ToastError; export default ToastError;

Exception type that prompts the developer:
devError.js

function DevError(message = 'Default Message') { this.name = 'ToastError'; this.message = message; this.stack = (new Error()).stack; } DevError.prototype = Object.create(Error.prototype); DevError.prototype.constructor = DevError; export default DevError;

Exception handler:
When a common exception is thrown, you can bring  a list of questions on stackoverflow  to help developers find the cause.
errorHandler.js

 
import EnsureError from './ensureError.js';
import ToastError from './toastError.js'; import DevError from './devError.js'; import EnsurePopup from './ensurePopup.js'; import ToastPopup from './toastPopup.js'; function errorHandler(err) { if (err instanceof EnsureError) { EnsurePopup(err.message); } else if (err instanceof ToastError) { ToastPopup(err.message); }else if( err instanceof DevError){ DevError(err.message); }else{ error.message += ` https://stackoverflow.com/questions?q=${encodeURI(error.message)}` console.error(err.message); } } window.onerror = (msg, url, line, col, err) => { errorHandler(err); } window.onunhandledrejection = event =>{ errorHandler(event.reason); }; export default errorHandler;

Original: https://segmentfault.com/a/1190000011481099

you may be interested in

  1. Detailed explanation of 10 major JavaScript errors from 1000+ project data analysis
  2. Where there is a browser, there is Fundebug
  3. This is how I understand a magical bug
  4. Debug front-end HTML/CSS

Guess you like

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