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 occurredcolno
: The column number (number) where the exception occurrederror
: 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 error
filter 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);
}
event
An event is an instance of PromiseRejectionEvent , which has two properties:
event.promise
: JavaScript Promise that was rejectedevent.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