How to catch AssertionError in a nested function in JavaScript? - javascript

I'm writing a mocha clone, because mocha is lacking certain features that I need. But when I am using existing assertion libraries, I'm unable to catch errors that are returned by them. Here is the code that runs the tests.
Tester.Test = function(name, test_function) {
this.name = name;
this.run = function(callback) {
try {
test_function(callback);
} catch (e) {
callback(e);
}
};
};
I've already tried using domain to catch the error, but it's still not working:
var d = domain.create();
d.on("error", function(err) {
cb(err);
});
d.run(function() {
test.run(cb);
});
I still keep getting an AssertionError (expected false to be true). Any tips?

Related

JavaScript Capture handled Errors

I am looking for a solution in which I can capture all the errors(handled/unhandled) logged in the browser console.
I am aware about window.onerror and also window.addeventlistener('error', function(){}).
The above code captures only unhandled errors. I need to capture handled errors also.
For example:
function foo() {
var x = null;
try {
x.a = "";
} catch (e) {
//Exception will be digested here.
}
var y = null
y.b = "";
}
window.onerror = function() {
//Write logic for the errors logged in console.
}
foo();
In the above example try catch is there, so I will get error only for variable y not x.
Is there any way to listen/capture the catch block?
Thanks
The reality is that if an exception is caught, then it isn't a problem because your code knows how to deal with it.
In other programming languages like Java it's always good practise to only catch exceptions you can handle, and to throw everything else further up the chain and/or map to a different exception that may be more useful further up the call stack.
For example:
function foo() {
var x = null;
try {
x.a = "";
} catch (e) {
//Exception will be digested here.
console.log("I am writing code here but still don't know how to proceed");
throw new CustomError("Something was wrong");
}
var y = null
y.b = "";
}
Try calling window.onerror manually. However, please reconsider changing your approach. This is extremely dirty.
window.onerror = function(msg, url, lineNo, columnNo, error) {
if (error === 'custom') {
console.log("Handled error logged");
return true;
}
console.log("unhandled error")
return false;
};
Example try/catch
var myalert = function() {
try {
console.log(f);
} catch (e) {
window.onerror('test',null,null,null,'custom');
}
console.log("Gets here");
}
https://fiddle.jshell.net/L29jv5fv/2/

Is it normal to throw exception from asynchronous methods?

I have some code that I want to refactor (extract server communication methods from controller to separate service).
Example:
$http.post("/mypath", someData)
.success(function(request) {
if (request.ok) {
$scope.error = "";
_refreshAppointments();
}
else {
$scope.error = request.err;
}
})
.error(function() {
$scope.error = "Error during communicating to server";
});
My current problem is errors processing (communication with old $scope). So I want to throw the exceptions instead such lines $scope.error = "Error during communicating to server";
And catch them in controller.
Is it good idea?
If you throw an error in a vanilla environment:
setTimeout(function () {
throw new Error();
}, 1);
The error just gets lost. (window.onerror will see it though)
Even if you do:
try {
setTimeout(function () {
throw new Error();
}, 1);
} catch (e) {
console.log(e);
}
You wont see the error.
You need to wrap each asynchronous event, like:
function mySetTimeout(callback, ms) {
setTimeout(wrap_in_try_catch(callback), ms);
}
mySetTimeout(function () {
throw new Error();
});
You can now catch the error in a generic error handler, but you still can't catch it in the surrounding code.
This is where Promises come in. Not all libraries do Promises the same way (or correctly) so I don't know how good your library support is.
Roughly your code will look like:
$.ajax().then(function () {
throw new Error();
}).fail(e) {
console.log("it failed!", e);
});
If instead you have:
$.ajax().then(function () {
throw new Error();
}); // throws something like: no fail callback for rejected value error
Then your global error handler will pick it up. This ensures no error can slip through the cracks and get lost.
Getting a Promise library to work with Errors in this way is not impossible but it's a little bit tricky to set up. Once you have this though, you're good. Error handling becomes a breeze.
You'll never write a try-catch again, just a bunch of .fail() handlers.
It's definitely good idea to extract REST/http requests into model/service layer and use those services from controller. Then handling failed operation would mean rejecting a corresponding promise, in this case throwing exception in promise effectively means the same.
For example this is how your service/factory could look like:
app.factory('dataService', function($http) {
return {
load: function() {
return $http.post("/mypath", someData).then(function(response) {
if (!response.data.ok) {
return throw new Error(response.request.err);
// or return $q.reject(response.request.err);
}
return response.request;
});
}
};
});
and consuming controller would deal with promise status, resolved (success) or rejected (failed/exception):
dataService.load().then(function(request) {
$scope.error = "";
_refreshAppointments();
})
.catch(function(err) {
$scope.error = err || "Error during communicating to server";
});

Stop error propagation in Bluebird promises

How can I stop a thrown error from propagating all the way down the chain? It shows in my catch() block but it doesn't stop and crashes the server with an uncaught exception.
I am running this as part of a node cron job (node-cron) as:
var cronJob = require('cron').CronJob;
var cron = require('../lib/cron')
var c = new cronJob('* * * * * *', function() {
console.log('Cron starting');
mycode.run();
}, function() {
console.log('Cron executed');
}, true);
c.start();
In my cron.js
module.exports = {
run: function() {
return job.getAndStore().catch(function(e) {
// This prints but it keeps on going so to speak - it doesn't 'catch', just notifies me
console.log('ERROR', e);
});
}
};
Console dump:
Cron starting
ERROR [TypeError: undefined is not a function]
Cron starting
Uncaught Exception
[TypeError: undefined is not a function]
TypeError: undefined is not a function
I have to do this which I know not quite right:
try {
run();
} catch(e) {
console.log('Now it stops')
}
The run() is part of some cron library that doesn't have any promise support so I am wrapping it in the function to call it.
Edit As I think my issue is related to subsequent calls I believe it has to do with how I handle the Mongo connection on 2+ calls:
// Create a Mongo connection
Job.prototype.getDb = function(id) {
var self = this;
return new P(function(resolve, reject) {
if (!self.db) {
return Mongo.connectAsync(self.options.connection)
.then(function(c) {
self.db = c;
debug('Got new connection');
resolve(c);
});
}
debug('Got existing connection');
resolve(self.db);
});
};
// Fetch stuff
Job.prototype.getAndStore = function(c) {
return this.getDb().then(function() {
throw new Error('Boom');
});
};
Your catch callback is only executed the first time. You are getting the uncaught exception in the second run of the cron job, and it looks like your job.getAndStore() does not return a rejected promise there but throws synchronously. It shouldn't, it should always return a promise.
You can use Bluebirds Promise.try to automatically catch such exceptions and transform them into a promise rejection. Or you wrap your getAndStore function in Promise.method:
var safeGetAndStore = Promise.method(job.getAndStore.bind(job));
module.exports = {
run: function() {
return safeGetAndStore().catch(function(e) {
console.log('ERROR', e);
});
}
};
In your specific case, the problem was that your job did cache the db connection and returned that when it was already available - but you needed to return a promise with a .then method. You should simply cache the promise itself:
Job.prototype.getDb = function(id) {
if (!this.db) {
this.db = Mongo.connectAsync(self.options.connection);
return this.db;
};
Use done, at least if bluebird implements it properly it will work as you expect.
catch(..) is just alias for then(null, ..) which is promise transformer that creates another promise for further processing.
So following should work for you:
module.exports = {
run: function() {
return job.getAndStore().done(null, function(e) {
// This prints but it keeps on going so to speak - it doesn't 'catch', just notifies me
console.log('ERROR', e);
});
}
};

How to chain Q promise with previous errors on finally()

I am wondering how to chain errors on Q using finally.
Consider following code
function p1() {
throw new Error("p1 error");
}
function p2() {
throw new Error("p2 error");
}
function p3() {
return Q.fcall(p1)
.finally(function () {
return Q.fcall(p2);
});
}
p3()
.done();
The Error with message "p1 error" is lost since it was override by Error "p2 error". How can I throw all the errors (or combine the errors)?
Currently, I am working on a socket connection on nodejs. I am using .finally() to close the socket after each connection.
However, errors (eg: authentication error) before .finally() will be overriden by one in .finally() (eg: connection close error). So, I am wondering how to get all errors
Thanks
You could do something like this:
function p3() {
return Q.fcall(p1)
.finally(function(p) {
return Q.fcall(p2).catch(function(e) {
if (!p.isRejected()) throw e;
var agg = new Error("multiple errors occured");
agg[0] = p.inspect().reason;
agg[1] = e;
agg.length = 2;
throw agg;
});
});
}
(based on Bluebird's AggregateError)
Of course it is tedious, but I can't imagine something better.

Handling specific errors in JavaScript (think exceptions)

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.");
});

Categories