This tutorial dives into JavaScript error dealing with so that you’ll be capable to throw, detect, and deal with your individual errors.
Contents:
- Showing an Error Message is the Last Resort
- How JavaScript Processes Errors
- Catching Exceptions
- Standard JavaScript Error Types
- AggregateError
- Throwing Our Own Exceptions
- Asynchronous Function Errors
- Promise-based Errors
- Exceptional Exception Handling
Knowledgeable builders anticipate the sudden. If one thing can go fallacious, it would go fallacious — usually, the second the primary consumer accesses your new internet system.
We will keep away from some internet utility errors like so:
- A superb editor or linter can catch syntax errors.
- Good validation can catch consumer enter errors.
- Strong check processes can spot logic errors.
But errors stay. Browsers could fail or not help an API we’re utilizing. Servers can fail or take too lengthy to reply. Community connectivity can fail or change into unreliable. Points could also be short-term, however we are able to’t code our method round such issues. Nonetheless, we are able to anticipate issues, take remedial actions, and make our utility extra resilient.
Exhibiting an Error Message is the Final Resort
Ideally, customers ought to by no means see error messages.
We could possibly ignore minor points, corresponding to an ornamental picture failing to load. We may handle extra severe issues corresponding to Ajax data-save failures by storing data locally and importing later. An error solely turns into mandatory when the consumer is vulnerable to dropping information — presuming they’ll do one thing about it.
It’s due to this fact essential to catch errors as they happen and decide the most effective motion. Elevating and catching errors in a JavaScript utility could be daunting at first, however it’s probably simpler than you anticipate.
How JavaScript Processes Errors
When a JavaScript assertion ends in an error, it’s mentioned to throw an exception. JavaScript creates and throws an Error
object describing the error. We will see this in motion in this CodePen demo. If we set the decimal locations to a unfavorable quantity, we’ll see an error message within the console on the backside. (Word that we’re not embedding the CodePens on this tutorial, since you want to have the ability to see the console output for them to make sense.)
The consequence received’t replace, and we’ll see a RangeError
message within the console. The next perform throws the error when dp
is unfavorable:
perform divide(v1, v2, dp)
return (v1 / v2).toFixed(dp);
After throwing the error, the JavaScript interpreter checks for exception dealing with code. None is current within the divide()
perform, so it checks the calling perform:
perform showResult()
consequence.worth = divide(
parseFloat(num1.worth),
parseFloat(num2.worth),
parseFloat(dp.worth)
);
The interpreter repeats the method for each perform on the decision stack till one in every of these items occurs:
- it finds an exception handler
- it reaches the highest stage of code (which causes this system to terminate and present an error within the console, as demonstrated within the CodePen instance above)
Catching Exceptions
We will add an exception handler to the divide()
perform with a try…catch block:
perform divide(v1, v2, dp)
strive
return (v1 / v2).toFixed(dp);
catch(e)
console.log(`
error identify : $ e.identify
error message: $ e.message
`);
return 'ERROR';
This executes the code within the strive
block however, when an exception happens, the catch
block executes and receives the thrown error object. As earlier than, strive setting the decimal locations to a unfavorable quantity in this CodePen demo.
The consequence now exhibits ERROR. The console exhibits the error identify and message, however that is output by the console.log
assertion and doesn’t terminate this system.
Word: this demonstration of a strive...catch
block is overkill for a fundamental perform corresponding to divide()
. It’s easier to make sure dp
is zero or greater, as we’ll see beneath.
We will outline an non-compulsory lastly
block if we require code to run when both the strive
or catch
code executes:
perform divide(v1, v2, dp)
strive
return (v1 / v2).toFixed(dp);
catch(e)
return 'ERROR';
lastly
console.log('executed');
The console outputs "executed"
, whether or not the calculation succeeds or raises an error. A lastly
block usually executes actions which we’d in any other case have to repeat in each the strive
and the catch
block — corresponding to cancelling an API name or closing a database connection.
A strive
block requires both a catch
block, a lastly
block, or each. Word that, when a lastly
block incorporates a return
assertion, that worth turns into the return worth for the entire perform; different return
statements in strive
or catch
blocks are ignored.
Nested Exception Handlers
What occurs if we add an exception handler to the calling showResult()
perform?
perform showResult()
strive
consequence.worth = divide(
parseFloat(num1.worth),
parseFloat(num2.worth),
parseFloat(dp.worth)
);
catch(e)
consequence.worth = 'FAIL!';
The reply is … nothing! This catch
block isn’t reached, as a result of the catch
block within the divide()
perform handles the error.
Nonetheless, we may programmatically throw a brand new Error
object in divide()
and optionally go the unique error in a trigger
property of the second argument:
perform divide(v1, v2, dp)
strive
return (v1 / v2).toFixed(dp);
catch(e)
throw new Error('ERROR', trigger: e );
It will set off the catch
block within the calling perform:
perform showResult()
strive
catch(e)
console.log( e.message );
console.log( e.trigger.identify );
consequence.worth = 'FAIL!';
Normal JavaScript Error Sorts
When an exception happens, JavaScript creates and throws an object describing the error utilizing one of many following varieties.
SyntaxError
An error thrown by syntactically invalid code corresponding to a lacking bracket:
if situation)
console.log('situation is true');
Word: languages corresponding to C++ and Java report syntax errors throughout compilation. JavaScript is an interpreted language, so syntax errors aren’t recognized till the code runs. Any good code editor or linter can spot syntax errors earlier than we try and run code.
ReferenceError
An error thrown when accessing a non-existent variable:
perform inc()
worth++;
Once more, good code editors and linters can spot these points.
TypeError
An error thrown when a price isn’t of an anticipated kind, corresponding to calling a non-existent object methodology:
const obj = ;
obj.missingMethod();
RangeError
An error thrown when a price isn’t within the set or vary of allowed values. The toFixed() method used above generates this error, as a result of it expects a price usually between 0 and 100:
const n = 123.456;
console.log( n.toFixed(-1) );
URIError
An error thrown by URI-handling capabilities corresponding to encodeURI() and decodeURI() after they encounter malformed URIs:
const u = decodeURIComponent('%');
EvalError
An error thrown when passing a string containing invalid JavaScript code to the eval() function:
eval('console.logg x;');
Word: please don’t use eval()
! Executing arbitrary code contained in a string probably constructed from consumer enter is way too harmful!
AggregateError
An error thrown when a number of errors are wrapped in a single error. That is usually raised when calling an operation corresponding to Promise.all(), which returns outcomes from any variety of guarantees.
InternalError
A non-standard (Firefox solely) error thrown when an error happens internally within the JavaScript engine. It’s usually the results of one thing taking an excessive amount of reminiscence, corresponding to a big array or “an excessive amount of recursion”.
Error
Lastly, there’s a generic Error
object which is most frequently used when implementing our personal exceptions … which we’ll cowl subsequent.
Throwing Our Personal Exceptions
We will throw
our personal exceptions when an error happens — or ought to happen. For instance:
- our perform isn’t handed legitimate parameters
- an Ajax request fails to return anticipated information
- a DOM replace fails as a result of a node doesn’t exist
The throw
assertion really accepts any worth or object. For instance:
throw 'A easy error string';
throw 42;
throw true;
throw message: 'An error', identify: 'MyError' ;
Exceptions are thrown to each perform on the decision stack till they’re intercepted by an exception (catch
) handler. Extra virtually, nonetheless, we’ll need to create and throw an Error
object in order that they act identically to plain errors thrown by JavaScript.
We will create a generic Error
object by passing an non-compulsory message to the constructor:
throw new Error('An error has occurred');
We will additionally use Error
like a perform with out new
. It returns an Error
object similar to that above:
throw Error('An error has occurred');
We will optionally go a filename and a line quantity because the second and third parameters:
throw new Error('An error has occurred', 'script.js', 99);
That is hardly ever mandatory, since they default to the file and line the place we threw the Error
object. (They’re additionally troublesome to keep up as our recordsdata change!)
We will outline generic Error
objects, however we must always use a standard Error type when potential. For instance:
throw new RangeError('Decimal locations should be 0 or larger');
All Error
objects have the next properties, which we are able to study in a catch
block:
.identify
: the identify of the Error kind — corresponding toError
orRangeError
.message
: the error message
The next non-standard properties are additionally supported in Firefox:
.fileName
: the file the place the error occurred.lineNumber
: the road quantity the place the error occurred.columnNumber
: the column quantity on the road the place the error occurred.stack
: a stack hint itemizing the perform calls made earlier than the error occurred
We will change the divide()
perform to throw a RangeError
when the variety of decimal locations isn’t a quantity, is lower than zero, or is bigger than eight:
perform divide(v1, v2, dp)
if (isNaN(dp)
Equally, we may throw an Error
or TypeError
when the dividend worth isn’t a quantity to forestall NaN
outcomes:
if (isNaN(v1))
throw new TypeError('Dividend should be a quantity');
We will additionally cater for divisors which might be non-numeric or zero. JavaScript returns Infinity when dividing by zero, however that might confuse customers. Moderately than elevating a generic Error
, we may create a customized DivByZeroError
error kind:
class DivByZeroError extends Error
constructor(message)
tremendous(message);
this.identify = 'DivByZeroError';
Then throw it in the identical method:
if (isNaN(v2) || !v2)
throw new DivByZeroError('Divisor should be a non-zero quantity');
Now add a strive...catch
block to the calling showResult()
perform. It could possibly obtain any Error
kind and react accordingly — on this case, exhibiting the error message:
perform showResult()
strive
consequence.worth = divide(
parseFloat(num1.worth),
parseFloat(num2.worth),
parseFloat(dp.worth)
);
errmsg.textContent = '';
catch (e)
consequence.worth = 'ERROR';
errmsg.textContent = e.message;
console.log( e.identify );
Attempt coming into invalid non-numeric, zero, and unfavorable values into this CodePen demo.
The ultimate model of the divide()
perform checks all of the enter values and throws an acceptable Error
when mandatory:
perform divide(v1, v2, dp) dp > 8)
throw new RangeError('Decimal locations should be between 0 and eight');
return (v1 / v2).toFixed(dp);
It’s not mandatory to position a strive...catch
block across the last return
, because it ought to by no means generate an error. If one did happen, JavaScript would generate its personal error and have it dealt with by the catch
block in showResult()
.
Asynchronous Operate Errors
We will’t catch exceptions thrown by callback-based asynchronous capabilities, as a result of an error is thrown after the strive...catch
block completes execution. This code appears to be like appropriate, however the catch
block won’t ever execute and the console shows an Uncaught Error
message after one second:
perform asyncError(delay = 1000)
setTimeout(() =>
throw new Error('I'm by no means caught!');
, delay);
strive
asyncError();
catch(e)
console.error('It will by no means run');
The conference presumed in most frameworks and server runtimes corresponding to Node.js is to return an error as the primary parameter to a callback perform. That received’t increase an exception, though we may manually throw an Error
if mandatory:
perform asyncError(delay = 1000, callback)
setTimeout(() =>
callback('That is an error message');
, delay);
asyncError(1000, e =>
if (e)
throw new Error(`error: $ e `);
);
Promise-based Errors
Callbacks can change into unwieldy, so it’s preferable to make use of promises when writing asynchronous code. When an error happens, the promise’s reject()
methodology can return a brand new Error
object or every other worth:
perform wait(delay = 1000) {
return new Promise((resolve, reject) =>
if (isNaN(delay) )
}
Word: capabilities should be both 100% synchronous or 100% asynchronous. That is why it’s essential to verify the delay
worth contained in the returned promise. If we checked the delay
worth and threw an error earlier than returning the promise, the perform would change into synchronous when an error occurred.
The Promise.catch() method executes when passing an invalid delay
parameter and it receives to the returned Error
object:
wait('INVALID')
.then( res => console.log( res ))
.catch( e => console.error( e.message ) )
.lastly( () => console.log('full') );
Personally, I discover promise chains a bit troublesome to learn. Happily, we are able to use await
to name any perform which returns a promise. This should happen inside an async
perform, however we are able to seize errors utilizing a regular strive...catch
block.
The next (instantly invoked) async
perform is functionally similar to the promise chain above:
(async () =>
strive
console.log( await wait('INVALID') );
catch (e)
console.error( e.message );
lastly
console.log('full');
)();
Distinctive Exception Dealing with
Throwing Error
objects and dealing with exceptions is straightforward in JavaScript:
strive
throw new Error('I'm an error!');
catch (e)
console.log(`error $ e.message `)
Constructing a resilient utility that reacts appropriately to errors and makes life straightforward for customers is tougher. All the time anticipate the sudden.
Additional data: