So recently I was asked a question :
Is there a way to throw an error without using throw in javaScript ?
As far as I knew about errors, there was only one way to throw an error in JavaScript and that was using throw statement in JavaScript like so :
var myFunc = () => {
// some code here
throw 'Some error' // in a conditional
// some more code
}
try {
myFunc()
}catch(e) {
console.log(e)
}
And not knowing any other way I said No, there is no other way. But now I'm wondering whether I was right ?
So the question is whether or not you can throw a custom error in JavaScript without using throw
Restrictions :
Kindly no using eval , Function.
Don't use throw in your code
Additional :
If you can throw an error without using the word Error
Generators do have a throw method that is usually used to throw an exception into the generator function code (at the place of a yield expression, similar to next), but if not caught it bubbles out of the call:
(function*(){})().throw(new Error("example"))
Of course, this is a hack and not good style, I have no idea what answer they expected. Especially the "no division by zero" requirement is sketchy, since division by zero does not throw exceptions in JS anyway.
If all you want to do is throw an error then just do an invalid operation. I've listed a few below. Run them on your browser console to see the errors (tested on chrome and firefox).
var myFunc = () => {
encodeURI('\uD800'); // URIError
a = l; // ReferenceError
null.f() // TypeError
}
try {
myFunc()
}catch(e) {
console.log(e);
}
function throwErrorWithoutThrow(msg) {
// get sample error
try {
NaN();
} catch (typeError) {
// aquire reference to Error
let E = typeError.__proto__.__proto__.constructor;
// prepare custom Error
let error = E(msg);
// throw custom Error
return Promise.reject(error);
}
}
throwErrorWithoutThrow("hi")
/* catch the error, you have to use .catch as this is a promise
that's the only drawback, you can't use try-catch to catch these errors */
.catch((e) => {
console.log("Caught You!");
console.error(e);
});
I think, if you want to use try...catch blocks, you will need to throw something to get to the catchpart. You can use pretty much everything that fails with an error to do that (like mentioned in the other posts).
If at some point, you want to run-or-die without having to write the throw statement yourself, you can always do an assertion:
const myFunction = () => {
try {
assert.fail();
console.log(`Did not work :)`);
} catch (e) {
console.log(`OK`);
}
};
Of course I would rather try to assert something positive (more Zen), that I need in order to continue.
const myTypeErr = () => `This is a Custom Err Message... \nand it`()
try {
myTypeErr()
} catch(e) {} // caught
myTypeErr() // Uncaught TypeError: "This is a Custom Err Message...
// and it" is not a function
Related
So I want to know how to make sure that my Node application will crash if it comes upon a programmer error(undefined variable, reference error, syntax error...). However, if I am using promise chains then the final catch() will catch all possible errors including programmer errors.
For example:
PromiseA()
.then((result) => {
foo.bar(); //UNDEFINED FUNCTION HERE!!!!!
return PromiseB(result);
})
.catch(
//handle errors here
)
Now the catch() statement will also catch the really bad error of an undefined function and then try to handle it. I need a way for my program to crash when it comes up against errors like these.
EDIT: I also just realized that even if I throw an error in the last catch it will just be consumed by the promise chain :-[. How am I supposed to deal with that?
Basically, what you want to do is to handle those errors that you can potentially recover from. These errors are usually something you throw in your code. For example, if an item is not found in a DB some libraries will throw an Error. They'll add a type property or some other property to differentiate the different type of errors.
You can also potentially subclass the Error class and use instanceof to differentiate each error.
class myOwnError extends Error {}
Then:
Prom.catch(err => {
if(err instanceof myOwnError){ /* handle error here */ }
else { throw err; }
});
If you want to avoid if/chains, you can use a switch on error.constructor:
switch(err.constructor){
case myOwnError:
break;
case someOtherError:
break;
default:
throw err;
}
You can also use an a Map or regular objects by creating functions for each possible error and storing them. With a Map:
let m = new Map();
m.set(myOWnError, function(e){ /*handle error here*/ });
m.set(myOtherError, function(e){ /*handle other error here*/ });
Then just do:
Prom.catch(err => {
let fn = m.get(err.constructor);
if(fn){ return fn(err); }
else { throw err; }
});
Disclaimer: Below is a description what we do at the company I work. The package linked is written by us.
What we do is to catch all errors and sort them into programmer and operational errors.
We've made small library to help us: https://www.npmjs.com/package/oops-error
For promise chains we use:
import { programmerErrorHandler } from 'oops-error'
...
export const doSomething = (params) => {
somePromiseFunction().catch(programmerErrorHandler('failed to do something', {params}))
}
It marks the error as programmer error, adds 'failed to do something' as error message and adds the params as a context (for debugging later)
For errors that we know that can come up (person not found, validEmail, etc) we do something like
import { Oops } from 'oops-error'
export const sendEmail = (email) => {
if(!isValidEmail(email)) {
throw new Oops({
message: 'invalid email',
category: 'OperationalError',
context: {
email,
},
})
}
...
}
At every level we show the error messages of Operational Errors. So a simple
.cath(e => {
if (e.category == 'OperationalError') {
// handle the gracefully
}
else {
throw e // We will tackle this later
}
}
And at the end of our request in express we have our final error handler, where catch the error, check if its operational and then show the error message, but not the actual context. If its a programmer error we stop the process (not ideal, but we don't want the user to keep messing with broken code)
Is there any way to define that a function explicitly can throw, obviously any function can throw an error. But I'd like to explicitly define that a function is designed to throw and possibly not return a value at all.
async function falseThrow (promise: Promise<any>, error): any {
let value: any = await promise
if (!value) throw error
return value
}
As Nat Mote highlighted in her answer, there's no way of encoding checked exceptions in flow.
However, if you're open to change the encoding a bit, you could do something equivalent:
async function mayFail<A, E>(promise: Promise<?A>, error: E): E | A {
let value = await promise
if (!value) {
return error
}
return value
}
Now flow will force the user to handle the error:
const p = Promise.resolve("foo")
const result = await mayFail(p, Error("something went wrong"))
if (result instanceof Error) {
// handle the error here
} else {
// use the value here
result.trim()
}
If you try to do something like
const p = Promise.resolve("foo")
const result = await mayFail(p, Error("something went wrong"))
result.trim() // ERROR!
flow will stop you, because you haven't checked whether result is an Error or a string, so calling trim is not a safe operation.
Flow doesn't support checked exceptions. It makes no attempt to model which functions may throw or what sorts of errors they may throw.
I am still fairly new to promises and am using bluebird currently, however I have a scenario where I am not quite sure how to best deal with it.
So for example I have a promise chain within an express app like so:
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
So the behaviour I am after is:
Goes to get account by Id
If there is a rejection at this point, bomb out and return an error
If there is no error convert the document returned to a model
Verify the password with the database document
If the passwords dont match then bomb out and return a different error
If there is no error change the passwords
Then return success
If anything else went wrong, return a 500
So currently catches do not seem to stop the chaining, and that makes sense, so I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors, or if there is a better way to structure this to get some form of branching behaviour, as there is a case of if X do Y else Z.
Any help would be great.
This behavior is exactly like a synchronous throw:
try{
throw new Error();
} catch(e){
// handle
}
// this code will run, since you recovered from the error!
That's half of the point of .catch - to be able to recover from errors. It might be desirable to rethrow to signal the state is still an error:
try{
throw new Error();
} catch(e){
// handle
throw e; // or a wrapper over e so we know it wasn't handled
}
// this code will not run
However, this alone won't work in your case since the error be caught by a later handler. The real issue here is that generalized "HANDLE ANYTHING" error handlers are a bad practice in general and are extremely frowned upon in other programming languages and ecosystems. For this reason Bluebird offers typed and predicate catches.
The added advantage is that your business logic does not (and shouldn't) have to be aware of the request/response cycle at all. It is not the query's responsibility to decide which HTTP status and error the client gets and later as your app grows you might want to separate the business logic (how to query your DB and how to process your data) from what you send to the client (what http status code, what text and what response).
Here is how I'd write your code.
First, I'd get .Query to throw a NoSuchAccountError, I'd subclass it from Promise.OperationalError which Bluebird already provides. If you're unsure how to subclass an error let me know.
I'd additionally subclass it for AuthenticationError and then do something like:
function changePassword(queryDataEtc){
return repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword);
}
As you can see - it's very clean and you can read the text like an instruction manual of what happens in the process. It is also separated from the request/response.
Now, I'd call it from the route handler as such:
changePassword(params)
.catch(NoSuchAccountError, function(e){
res.status(404).send({ error: "No account found with this Id" });
}).catch(AuthenticationError, function(e){
res.status(406).send({ OldPassword: error });
}).error(function(e){ // catches any remaining operational errors
res.status(500).send({ error: "Unable to change password" });
}).catch(function(e){
res.status(500).send({ error: "Unknown internal server error" });
});
This way, the logic is all in one place and the decision of how to handle errors to the client is all in one place and they don't clutter eachother.
.catch works like the try-catch statement, which means you only need one catch at the end:
repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error) {
if (/*see if error is not found error*/) {
res.status(404).send({ error: "No account found with this Id" });
} else if (/*see if error is verification error*/) {
res.status(406).send({ OldPassword: error });
} else {
console.log(error);
res.status(500).send({ error: "Unable to change password" });
}
});
I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors
No. You cannot really "end" a chain, unless you throw an exception that bubbles until its end. See Benjamin Gruenbaum's answer for how to do that.
A derivation of his pattern would be not to distinguish error types, but use errors that have statusCode and body fields which can be sent from a single, generic .catch handler. Depending on your application structure, his solution might be cleaner though.
or if there is a better way to structure this to get some form of branching behaviour
Yes, you can do branching with promises. However, this means to leave the chain and "go back" to nesting - just like you'd do in an nested if-else or try-catch statement:
repository.Query(getAccountByIdQuery)
.then(function(account) {
return convertDocumentToModel(account)
.then(verifyOldPassword)
.then(function(verification) {
return changePassword(verification)
.then(function() {
res.status(200).send();
})
}, function(verificationError) {
res.status(406).send({ OldPassword: error });
})
}, function(accountError){
res.status(404).send({ error: "No account found with this Id" });
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
I have been doing this way:
You leave your catch in the end. And just throw an error when it happens midway your chain.
repository.Query(getAccountByIdQuery)
.then((resultOfQuery) => convertDocumentToModel(resultOfQuery)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then((model) => verifyOldPassword(model)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch((error) => {
if (error.name === 'no_account'){
res.status(404).send({ error: "No account found with this Id" });
} else if (error.name === 'wrong_old_password'){
res.status(406).send({ OldPassword: error });
} else {
res.status(500).send({ error: "Unable to change password" });
}
});
Your other functions would probably look something like this:
function convertDocumentToModel(resultOfQuery) {
if (!resultOfQuery){
throw new Error('no_account');
} else {
return new Promise(function(resolve) {
//do stuff then resolve
resolve(model);
}
}
Probably a little late to the party, but it is possible to nest .catch as shown here:
Mozilla Developer Network - Using Promises
Edit: I submitted this because it provides the asked functionality in general. However it doesn't in this particular case. Because as explained in detail by others already, .catch is supposed to recover the error. You can't, for example, send a response to the client in multiple .catch callbacks because a .catch with no explicit return resolves it with undefined in that case, causing proceeding .then to trigger even though your chain is not really resolved, potentially causing a following .catch to trigger and sending another response to the client, causing an error and likely throwing an UnhandledPromiseRejection your way. I hope this convoluted sentence made some sense to you.
Instead of .then().catch()... you can do .then(resolveFunc, rejectFunc). This promise chain would be better if you handled things along the way. Here is how I would rewrite it:
repository.Query(getAccountByIdQuery)
.then(
convertDocumentToModel,
() => {
res.status(404).send({ error: "No account found with this Id" });
return Promise.reject(null)
}
)
.then(
verifyOldPassword,
() => Promise.reject(null)
)
.then(
changePassword,
(error) => {
if (error != null) {
res.status(406).send({ OldPassword: error });
}
return Promise.Promise.reject(null);
}
)
.then(
_ => res.status(200).send(),
error => {
if (error != null) {
console.error(error);
res.status(500).send({ error: "Unable to change password" });
}
}
);
Note: The if (error != null) is a bit of a hack to interact with the most recent error.
I think Benjamin Gruenbaum's answer above is the best solution for a complex logic sequence, but here is my alternative for simpler situations. I just use an errorEncountered flag along with return Promise.reject() to skip any subsequent then or catch statements. So it would look like this:
let errorEncountered = false;
someCall({
/* do stuff */
})
.catch({
/* handle error from someCall*/
errorEncountered = true;
return Promise.reject();
})
.then({
/* do other stuff */
/* this is skipped if the preceding catch was triggered, due to Promise.reject */
})
.catch({
if (errorEncountered) {
return;
}
/* handle error from preceding then, if it was executed */
/* if the preceding catch was executed, this is skipped due to the errorEncountered flag */
});
If you have more than two then/catch pairs, you should probably use Benjamin Gruenbaum's solution. But this works for a simple set-up.
Note that the final catch only has return; rather than return Promise.reject();, because there's no subsequent then that we need to skip, and it would count as an unhandled Promise rejection, which Node doesn't like. As is written above, the final catch will return a peacefully resolved Promise.
I wanted to preserve the branching behaviour that Bergi's answer had, yet still provide the clean code structure of unnested .then()'s
If you can handle some ugliness in the machinery that makes this code work, the result is a clean code structure similar to non-nested chained .then()'s
One nice part of structuring a chain like this, is that you can handle all the potential results in one place by chainRequests(...).then(handleAllPotentialResults) this might be nice if you need to hide the request chain behind some standardised interface.
const log = console.log;
const chainRequest = (stepFunction, step) => (response) => {
if (response.status === 200) {
return stepFunction(response, step);
}
else {
log(`Failure at step: ${step}`);
return response;
}
};
const chainRequests = (initialRequest, ...steps) => {
const recurs = (step) => (response) => {
const incStep = step + 1;
const nextStep = steps.shift();
return nextStep ? nextStep(response, step).then(chainRequest(recurs(incStep), incStep)) : response;
};
return initialRequest().then(recurs(0));
};
// Usage
async function workingExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
async function failureExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/fail'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
console.log(await workingExample());
console.log(await failureExample());
The idea is there, but the interface exposed could probably use some tweaking.
Seeing as this implementation used curried arrow functions, the above could potentially be implemented with more direct async/await code
i have this function in my code and I'm using throw to create meaningful errors (rather than failing silently). However, when i structure my function this way, if i call defineSandbox() with an error, it stops the whole script.
//defineSandbox is in an object, for now i name "myObj"
var defineSandbox = function (sandboxName,SandboxDefinition) {
//validation
if(!is.validString(sandboxName)) {
throw statusMessages.sandbox.invalidName;
}
if(!is.validFunction (SandboxDefinition)) {
throw statusMessages.sandbox.invalidDefinition;
}
//...some more validation here
//...sandbox builder code if validation passes (code wasn't returned)
registered.sandboxes[sandboxName] = newSandbox;
};
//intentional error, non-string name
myObj.defineSandbox(false);
//...and so the rest of the script from here down is not executed
//i can't build this sandbox anymore
myObj.defineSandbox('mySandbox',function(){...});
what i would like to have is if one call fails, it gives out an error but still tries to continue to run the script. how do i structure this code so that i can achieve that?
Typically, you don't want to continue execution when you manually throw a new error. If you want to have it for a logging purpose, you should consider an self-created internal solution.
However, to catch a thrown error you need to invoke a try / catch statement.
try {
myObj.defineSandbox(false);
} catch( ex ) {
// execution continues here when an error was thrown. You can also inspect the `ex`ception object
}
By the way, you can also specify which kind of error you want to throw, for instance a type error or a reference error like
throw new TypeError();
throw new ReferenceError();
throw new SyntaxError();
etc.
Complete list: MDN
console.error() will not throw an error, but will display an error in your log without halting execution.
If you would like to report the caught error so that window.onerror will fire, you can dispatch an error event in your catch block:
try {
}
catch (error)
{
const e = new ErrorEvent('error', {message:'my error', error:error})
window.dispatchEvent(e)
}
I found this useful for catching and reporting errors in a for loop while still continuing with the loop.
If you are trying to aggregate all the errors instead of just throwing one of them, then you should create an array of the issues (exceptions) and then throw the array, instead of just the first issue you hit.
You need to catch thrown errors if you want to deal with them nicely. In your example:
//intentional error, non-string name
try {
myObj.defineSandbox(false);
} catch(error) {
alert("Error defining sandbox: " + error);
}
And then subsequent code will still continue to run.
var bear = {};
(function () {
bear.errorProcesser = function ( e ) {
console.log( e );
}
bear.define = function ( name, fn ) {
try {
if( typeof name != "string" || typeof fn != "function"){
throw new Error ("some error");
}
bear[name] = fn;
} catch (e){
bear.errorProcesser ( e );
}
}
})()
bear.define ( "testfunc", {} );
Is it just try-catch you're searching for?
How would you implement different types of errors, so you'd be able to catch specific ones and let others bubble up..?
One way to achieve this is to modify the prototype of the Error object:
Error.prototype.sender = "";
function throwSpecificError()
{
var e = new Error();
e.sender = "specific";
throw e;
}
Catch specific error:
try
{
throwSpecificError();
}
catch (e)
{
if (e.sender !== "specific") throw e;
// handle specific error
}
Have you guys got any alternatives?
To create custom exceptions, you can inherit from the Error object:
function SpecificError () {
}
SpecificError.prototype = new Error();
// ...
try {
throw new SpecificError;
} catch (e) {
if (e instanceof SpecificError) {
// specific error
} else {
throw e; // let others bubble up
}
}
A minimalistic approach, without inheriting from Error, could be throwing a simple object having a name and a message properties:
function throwSpecificError() {
throw {
name: 'SpecificError',
message: 'SpecificError occurred!'
};
}
// ...
try {
throwSpecificError();
} catch (e) {
if (e.name == 'SpecificError') {
// specific error
} else {
throw e; // let others bubble up
}
}
As noted in the comments below this is Mozilla specific, but you can use 'conditional catch' blocks. e.g.:
try {
...
throwSpecificError();
...
}
catch (e if e.sender === "specific") {
specificHandler(e);
}
catch (e if e.sender === "unspecific") {
unspecificHandler(e);
}
catch (e) {
// don't know what to do
throw e;
}
This gives something more akin to typed exception handling used in Java, at least syntactically.
try-catch-finally.js
Using try-catch-finally.js, you can call the _try function with an anonymous callback, which it will call, and you can chain .catch calls to catch specific errors, and a .finally call to execute either way.
Example
_try(function () {
throw 'My error';
})
.catch(Error, function (e) {
console.log('Caught Error: ' + e);
})
.catch(String, function (e) {
console.log('Caught String: ' + e);
})
.catch(function (e) {
console.log('Caught other: ' + e);
})
.finally(function () {
console.log('Error was caught explicitly');
});
Example with modern arrow functions and template literals
_try(() => {
throw 'My error';
}).catch(Error, e => {
console.log(`Caught Error: ${e}`);
}).catch(String, e => {
console.log(`Caught String: ${e}`);
}).catch(e => {
console.log(`Caught other: ${e}`);
}).finally(() => {
console.log('Error was caught explicitly');
});
There is unfortunately no "official" way to achieve this basic functionality in Javascript. I'll share the three most common solutions I've seen in different packages, and how to implement them in modern Javascript (es6+), along with some of their pros and cons.
1. Subclass the Error class
Subclassing an instance of "Error" has become much easier in es6. Just do the following:
class FileNotFoundException extends Error {
constructor(message) {
super(message);
// Not required, but makes uncaught error messages nicer.
this.name = 'FileNotFoundException';
}
}
Complete example:
class FileNotFoundException extends Error {
constructor(message) {
super(message);
// Not required, but makes uncaught error messages nicer.
this.name = 'FileNotFoundException';
}
}
// Example usage
function readFile(path) {
throw new FileNotFoundException(`The file ${path} was not found`);
}
try {
readFile('./example.txt');
} catch (err) {
if (err instanceof FileNotFoundException) {
// Handle the custom exception
console.log(`Could not find the file. Reason: ${err.message}`);
} else {
// Rethrow it - we don't know how to handle it
// The stacktrace won't be changed, because
// that information is attached to the error
// object when it's first constructed.
throw err;
}
}
If you don't like setting this.name to a hard-coded string, you can instead set it to this.constructor.name, which will give the name of your class. This has the advantage that any subclasses of your custom exception wouldn't need to also update this.name, as this.constructor.name will be the name of the subclass.
Subclassed exceptions have the advantage that they can provide better editor support (such as autocomplete) compared to some of the alternative solutions. You can easily add custom behavior to a specific exception type, such as additional functions, alternative constructor parameters, etc. It also tends to be easier to support typescript when providing custom behavior or data.
There's a lot of discussion about how to properly subclass Error out there. For example, the above solution might not work if you're using a transpiler. Some recommend using the platform-specific captureStackTrace() if it's available (I didn't notice any difference in the error when I used it though - maybe it's not as relevant anymore 🤷♂️). To read up more, see this MDN page and This Stackoverflow answer.
Many browser APIs go this route and throw custom exceptions (as can be seen here)
Note that babel doesn't support this solution very well. They had to make certain trade-offs when transpiling class syntax (because it's impossible to transpile them with 100% accuracy), and they chose to make instanceof checks broken on babel-transpiled classes. Some tools, like TypeScript, will indirectly use babel, and will thus suffer from the same issues depending on how you've configured your TypeScript setup. If you run this in TypeScript's playground with its default settings today (March 2022), it will log "false":
class MyError extends Error {}
console.log(MyError instanceof Error);
2. Adding a distinguishing property to the Error
The idea is really simple. Create your error, add an extra property such as "code" to your error, then throw it.
const error = new Error(`The file ${path} was not found`);
error.code = 'NotFound';
throw error;
Complete example:
function readFile(path) {
const error = new Error(`The file ${path} was not found`);
error.code = 'NotFound';
throw error;
}
try {
readFile('./example.txt');
} catch (err) {
if (err.code === 'NotFound') {
console.log(`Could not find the file. Reason: ${err.message}`);
} else {
throw err;
}
}
You can, of course, make a helper function to remove some of the boilerplate and ensure consistency.
This solution has the advantage that you don't need to export a list of all possible exceptions your package may throw. You can imagine how awkward that can get if, for example, your package had been using a NotFound exception to indicate that a particular function was unable to find the intended resource. You want to add an addUserToGroup() function that ideally would throw a UserNotFound or GroupNotFound exception depending on which resource wasn't found. With subclassed exceptions, you'll be left with a sticky decision to make. With codes on an error object, you can just do it.
This is the route node's fs module takes to exceptions. If you're trying to read a non-existent file, it'll throw an instance of Error with some additional properties, such as code, which it'll set to "ENOENT" for that specific exception.
3. Return your exception.
Who says you have to throw them? In some scenarios, it might make the most sense to just return what went wrong.
function readFile(path) {
if (itFailed()) {
return { exCode: 'NotFound' };
} else {
return { data: 'Contents of file' };
}
}
When dealing with a lot of exceptions, a solution such as this could make the most sense. It's simple to do, and can help self-document which functions give which exceptions, which makes for much more robust code. The downside is that it can add a lot of bloat to your code.
complete example:
function readFile(path) {
if (Math.random() > 0.5) {
return { exCode: 'NotFound' };
} else {
return { data: 'Contents of file' };
}
}
function main() {
const { data, exCode } = readFile('./example.txt');
if (exCode === 'NotFound') {
console.log('Could not find the file.');
return;
} else if (exCode) {
// We don't know how to handle this exCode, so throw an error
throw new Error(`Unhandled exception when reading file: ${exCode}`);
}
console.log(`Contents of file: ${data}`);
}
main();
A non-solution
Some of these solutions feel like a lot of work. It's tempting to just throw an object literal, e.g. throw { code: 'NotFound' }. Don't do this! Stack trace information gets attached to error objects. If one of these object literals ever slips through and becomes an uncaught exception, you won't have a stacktrace to know where or how it happened. Debugging in general will be much more difficult. Some browsers may show a stacktrace in the console if one of these objects go uncaught, but this is just an optional convinience they provide, not all platforms provide this convinience, and it's not always accurate, e.g. if this object got caught and rethrown the browser will likely give the wrong stacktrace.
Upcoming solutions
The JavaScript committee is working on a couple of proposals that will make exception handling much nicer to work with. The details of how these proposals will work are still in flux, and are actively being discussed, so I won't dive into too much detail until things settle down, but here's a rough taste of things to come:
The biggest change to come will be the Pattern Matching proposal, which is intended to be a better "switch", among other things. With it, you'd easily be able to match against different styles of errors with simple syntax.
Here's a taste of what this might look like:
try {
...
} catch (err) {
match (err) {
// Checks if `err` is an instance of UserNotFound
when (${UserNotFound}): console.error('The user was not found!');
// Checks if it has a correct code property set to "ENOENT"
when ({ code: 'ENOENT' }): console.error('...');
// Handles everything else
else: throw err;
}
}
Pattern matching with the return-your-exception route grants you the ability to do exception handling in a style very similar to how it's often done in functional languages. The only thing missing would be the "either" type, but a TypeScript union type fulfills a very similar role.
const result = match (divide(x, y)) {
// (Please refer to the proposal for a more in-depth
// explanation of this syntax)
when ({ type: 'RESULT', value }): value + 1
when ({ type: 'DivideByZero' }): -1
}
There's also some very early discussion about bringing this pattern-matching syntax right into the try-catch syntax, to allow you to do something akin to this:
try {
doSomething();
} CatchPatten (UserNotFoundError & err) {
console.error('The user was not found! ' + err);
} CatchPatten ({ type: 'ENOENT' }) {
console.error('File not found!');
} catch (err) {
throw err;
}
Update
For those who needed a way to self-document which functions threw which exceptions, along with ways to ensure this self-documentation stayed honest, I previously recommended in this answer a small package I threw together to help you keep track of which exceptions a given function might throw. While this package does the job, now days I would simply recommend using TypeScript with the "return your exception" route for maximum exception safety. With the help of a TypeScript union type, you can easily document which exceptions a particular function will return, and TypeScript can help you keep this documentation honest, giving you type-errors when things go wrong.
Module for export usage
/**
* Custom InputError
*/
class InputError extends Error {
/**
* Create InputError
* #param {String} message
*/
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* Custom AuthError
*/
class AuthError extends Error {
/**
* Create AuthError
* #param {String} message
*/
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* Custom NotFoundError
*/
class NotFoundError extends Error {
/**
* Create NotFoundError
* #param {String} message
*/
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = {
InputError: InputError,
AuthError: AuthError,
NotFoundError: NotFoundError
};
Import into script:
const {InputError, AuthError, NotFoundError} = require(path.join(process.cwd(), 'lib', 'errors'));
Use:
function doTheCheck = () =>
checkInputData().then(() => {
return Promise.resolve();
}).catch(err => {
return Promise.reject(new InputError(err));
});
};
Calling code external:
doTheCheck.then(() => {
res.send('Ok');
}).catch(err => {
if (err instanceof NotFoundError) {
res.status(404).send('Not found');
} else if (err instanceof AuthError) {
res.status(301).send('Not allowed');
} else if (err instanceof InputError) {
res.status(400).send('Input invalid');
} else {
console.error(err.toString());
res.status(500).send('Server error');
}
});
An older question, but in modern JS (as of late 2021) we can do this with a switch on the error's prototype constructor in the catch block, simply matching it directly to any and all error classes we're interested in rather than doing instanceof checks, taking advantage of the fact that while instanceof will match entire hierarchies, identity checks don't:
import { SomeError } from "library-that-uses-errors":
import MyErrors from "./my-errors.js";
try {
const thing = someThrowingFunction();
} catch (err) {
switch (err.__proto__.constuctor) {
// We can match against errors from libraries that throw custom errors:
case (SomeError): ...
// or our own code with Error subclasses:
case (MyErrors.SOME_CLASS_THAT_EXTENDS_ERROR): ..
// and of course, we can check for standard built-in JS errors:
case (TypeError): ...
// and finally, if we don't know what this is, we can just
// throw it back and hope something else deals with it.
default: throw err;
}
}
(Of course, we could do this with an if/elseif/else too if switches are too "I hate having to use break everywhere", which is true for a lot of folks)
I didn't love any of these solutions so I made my own. The try-catch-finally.js is pretty cool except that if you forget one little underscore (_) before the try then the code will still run just fine, but nothing will get caught ever! Yuck.
CatchFilter
I added a CatchFilter in my code:
"use strict";
/**
* This catches a specific error. If the error doesn't match the errorType class passed in, it is rethrown for a
* different catch handler to handle.
* #param errorType The class that should be caught
* #param funcToCall The function to call if an error is thrown of this type
* #return {Function} A function that can be given directly to the `.catch()` part of a promise.
*/
module.exports.catchOnly = function(errorType, funcToCall) {
return (error) => {
if(error instanceof errorType) {
return funcToCall(error);
} else {
// Oops, it's not for us.
throw error;
}
};
};
Now I can filter
Now I can filter like in C# or Java:
new Promise((resolve, reject => {
<snip><snip>
}).catch(CatchFilter.catchOnly(MyError, err =>
console.log("This is for my error");
}).catch(err => {
console.log("This is for all of the other errors.");
});