I'm writing an app using knockout and I'd like to be able to catch any errors in my code which are run inside of knockout, for example a subscription.
Currently I have a knockout subscription:
var myObservable = ko.observable();
myObservable.subscribe(function (val) {
// Code here is error prone
});
I'd like to be able to use the above pattern throughout my app but be able to catch any errors thrown in the subscription callback.
My current solution is to wrap the ko.subbscribable.fn.subscribe function with an error handler, like so:
var _subscribe = ko.subscribable.fn.subscribe;
ko.subscribable.fn.subscribe = function (callback) {
if (arguments.length != 1) return _subscribe.apply(this, arguments);
else return _subscribe.apply(this, [function () {
try
{
callback.apply(this, arguments);
}
catch (err) {
// handleError is a function in my code which will handle the error for me
handleError(err);
}
}]);
};
My question is are there any hidden side affects with this approach, or is there a better approach I'm missing.
This is good approach, basically called duck punching, see great explanation from Paul Irish
http://www.paulirish.com/2010/duck-punching-with-jquery/
I would only update the code to handle error also in case more arguments are passed into subscribe function, like this
var _subscribe = ko.subscribable.fn.subscribe;
ko.subscribable.fn.subscribe = function () {
try {
return _subscribe.apply(this, arguments);
}
catch (err) {
// handleError is a function in my code which will handle the error for me
handleError(err);
}
};
Related
I have the following switch statement in several places in my code:
try {
...
} catch(e) {
switch(x){
case A:
throw e;
default:
return e;
}
}
I would love to be able to wrap it in a function like this:
function handle(e) {
switch(x) {
case A:
throw e;
default:
return e;
}
}
try {
...
} catch(e) {
handle(e);
}
I know the above case doesn't work (the code executing "handle" still doesn't throw or return anything) but it would be really nice to make the code a little more DRY.
Generally speaking, this shouldn't be a problem. Your example is a bit generic (e. g.: What is A and x? Do they need to be passed or can you derive them from the error?), so this example provides a general "solution" as well which (hopefully) suits your needs.
You could consider creating a "error-case" handler for various types of errors. The handler only every throws the error, if it cannot find a defined error case.
// Higher-order function to create "error handlers":
function handleBy (cases) {
return function (err) {
if (cases[err.constructor.name]) {
return cases[err.constructor.name](err);
}
throw err;
}
}
// create an error handler which returns different "defaults" or "fallbacks"
// for certain types of errors. alternatively, it returns the error itself
var handleRead = handleBy({
TypeError: function (err) {
return 'default value';
},
SyntaxError: function (err) {
return err;
}
});
// this is the action to be guarded
var unsafeAction = function () {
try {
return window.foo.bar;
} catch (e) {
return handleRead(e);
}
}
console.log(unsafeAction())
I see a piece of code similar to the one below in some npm package:
this.func(callback).then(function() {
...
return x;
}).then(function() {
...
return y;
}).then(function() {
...
return z;
}).then(function() {
mocha.run(function(failures) {
...
callback(failures);
});
}).catch(callback);
Questions:
What is the meaning of this catch(callback) with no {...} block following it?
I would like to add a finally clause to execute the callback, but every syntax that I'm trying seems to fail:
.catch(callback).finally(callback);
.catch(callback).finally(callback());
.catch(callback).finally{callback()};
.catch(callback).finally(){callback()};
In your case, then and catch refer to Promise's prototype and not to the native catch implementation. Check this example to understand it better:
let doSomething = () {
return new Promise((resolve, reject) => {
try {
reject();
} catch(e) {
reject();
} finally {
console.log('done');
}
});
}
doSomething().then(() => {}).catch(() => {});
Note that anything you'd do, catch will be called.
In your case catch function refers to your callback function which you are passing in catch block
//first case
function callback(){}
catch(callback);
//second case
catch(function(){})
Both case will work
and for finally It is still lacking browser support, you can check here at bottom of this page
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
and check this for how to do finally in alternative way.
what is the equivalent of bluebird Promise.finally in native ES6 promises?
you can try like this.
For more detail on promise: see here
doSomething(()=>{//do something})
.then(()=>{})
.catch((error)=> { console.log(error); })
.finally(()=> { //finally block here });
Question 1: The Promise API calls the function passed in the catch whenever a promise gets "rejected". So consider the following code:
// method 1: define the callback function
var callback = function(error){
console.error(error); // console the error
};
this.func.then(...).catch(callback);
// method 2: pass the function assigned to "callback" variable itself
this.func.then(...).catch(function(error){
console.error(error); // console the error
});
You are just telling the promise returned by the above (in your code) function call(s) that:
"Hey, whenever you fail to do the task, call this function callback that I (or someone else) have defined."
Question 2: The first method in your list of four methods should work.
I have the following files:-
target.js
var target = function(repository, logger) {
return {
addTarget : function(target) {
repository.add(target).then(
function (newTarget) {
console.log("added");
logger.info("added");
},
function (err) {
console.log("error");
logger.info("error");
}
);
}
};
};
module.exports = target;
targetTest.js
var chai = require("chai"),
expect = chai.expect,
sinon = require("sinon"),
Promise = require("bluebird"),
baseTarget = require("../target");
describe("target", function(){
it("should log error when it occurs", function() {
var mockRepository = {
add : sinon.stub().returns(Promise.reject(new Error()))
};
var mockLogger = {
info : sinon.spy()
};
var target = baseTarget(mockRepository, mockLogger);
target.addTarget("target");
expect(mockLogger.info.calledWith("error")).to.be.true;
});
});
The issue I have is that expect(mockLogger.info.calledWith("error")).to.be.true; returns false because add method on the repository is async and so hasn't executed yet. Is there a pattern for doing this properly.
This is really more of a question about 'how Promises work' than how they work within test frameworks - the answer to which is that their behaviour remains exactly the same.
Is there a pattern for doing this properly.
It is not so much a pattern as it is what Promises are built to do. Each success handler of a then is executed in sequence on success of the last. In your code we can return the Promise created by calling repository#add as you would if you wanted to use its result or perform some external dependent operation outside of addTarget:
addTarget: function (target) {
return repository
// ^^^^^^
.add(target)
.then(function (newTarget) {
console.log("added");
logger.info("added");
}, function (err) {
console.log("error");
logger.info("error");
});
}
Then place your expectation inside a then that will be executed on success of all members of the Promise chain created in addTarget:
target.addTarget("target").then(function () {
expect(mockLogger.info.calledWith("error")).to.be.true;
cb();
});
Asynchronous Tests
You will notice in the example above that there is also a call to a function cb. Due to your test being asynchronous you need to 'tell' the test framework when the test has completed. This is most often done by declaring your it function with a parameter, from which the framework will infer that the test is asynchronous and pass in a callback:
describe("target", function () {
it("should log error when it occurs", function (cb) {
// ^^^^
});
});
Assume I got an Ember obj. When doing any kind of sync with backend there is a possiblity to use a promise chain:
obj.save().then(function(res){
// Success callback
}, function(res){
// Fail callback
});
Is there a done/always callback for Ember.js promise chain with .then()?
I've tried adding a third parameter function, but it did not help.
http://emberjs.com/api/classes/Ember.PromiseProxyMixin.html#method_finally
Ember -> jQuery
.then() -> .done()
.catch() -> .fail()
.finally() -> .always()
Example (in the router):
var self = this;
var modelType = this.store.createRecord('modelType', {/* model attrs */});
modelType.save().then(function(model){
self.transitionTo('model.show', model);
}).catch(function(){
console.log('Failure to Save: ', reason);
}).finally({
self.hideSpinner()
});
Unfortunately there isn't. But you can create your own modifying the RSVP.Promise prototype:
Ember.RSVP.Promise.prototype.always = function(func) {
return this.then(func, func);
}
So you can do the following:
// will show success
Ember.RSVP.resolve('success').always(function(msg) {
alert(msg)
})
// will show error
Ember.RSVP.reject('error').always(function(msg) {
alert(msg)
})
I hope it helps
Ember uses the RSVP.js library for promises, and RSVP does not support always due to not being part of the Promises/A(+) specs.
If you need it, #wycats suggests the following approach:
Ember.RSVP.Promise.prototype.andThen = function(success, error, always) {
return this.then(function(value) {
var ret = success(value);
always(value);
return ret;
}, function(reason) {
var ret = error(reason);
always(reason);
return ret;
});
};
gorner's solution works but for Ember Data you have to add the following as well:
Ember.PromiseProxyMixin.reopen({
andThen: function() {
var promise = this.get('promise');
return promise['andThen'].apply(promise, arguments);
}
});
The reason is that the DS.Model.save() function returns a PromiseObject (see http://emberjs.com/api/data/classes/DS.PromiseObject.html), which doesn't implement Ember.RSVP.Promise but instead implements Ember.PromiseProxyMixin. So you have to make the andThen function available in that mixin in order for it to work with promises when saving models.
My group is starting a new project. We are thinking about organizing the JavaScript in such a way so that any JavaScript errors don't get lost but rather get caught and sent to the server to be logged. For namespacing I want to keep it simple so I'm using something like this:
var my_namespace = function() {
function myFunction(input) {
if (input < 0) {
throw "input must be positive";
}
return 'result';
}
return {myFunction: myFunction};
} ();
So now I can invoke my_namespace.myFunction(-22) but when the error will be thrown it will get lost. There will be many namespaces each one in it's own .js file (maybe somebody has a better idea about namespace schema).
So, my question is, how to like "surround" namespaces so that errors will not get lost?
Actually #Relic gave a good idea. I'm going to write below the code that will create the namespace "my_namespace" and surround the initialization by jQuery with try-catch:
var my_namespace = function() {
function init() {
throw "an exception during initialization";
}
return {init: init};
} ();
$(document).ready(function() {
try {
my_namespace.init();
} catch (e) {
// handle error
}
});
I'm going to experiment with what happens after it does initialization, that is, with the event handling.
Yep, just as I thought, event handling exceptions will not be caught. I'll research some more and return.
Two options for you:
Wrap Everything
You can wrap all of your code with try/catch blocks. This isn't as tedious as it sounds. There are two aspects of this: Wrapping your main code, and wrapping code that runs in response to events (user events, timer events, etc.). You can either do that manually, or you can give yourself a framework for doing it.
This doesn't have to be a pain at all. For instance, for the first part, just wrap a try/catch around your main code:
(function() { // (If you don't use scoping functions, just ignore this and the last line
try {
// Your code here
}
catch (e) {
reportException(e);
}
function reportException(exception) {
try {
// Do whatever you want to do to report the exception here.
}
catch (e) {
// Let the browser report it
throw 'Error handling exception: ' + exception;
}
}
})();
For the second part (catching exceptions in event handlers and code fired with setTimeout and similar), you can either always manually use try/catch blocks in all of your code (which is frequently what you want to do anyway), and possibly use a central function that wraps your event handlers to make sure uncaught exceptions are caught and handled, like this:
function makeHandler(handler) {
eventHandler.original = handler;
return eventHandler;
function eventHandler(event) {
try {
// Trigger the handler
return handler.call(this, event);
}
catch (e) {
// Handle event handler exception
reportException(e);
}
}
}
(There are more features you might add to that, but those are the basics.)
For public methods, you can use something quite similar to makeHandler:
function makePublic(method) {
publicMethod.original = method;
return publicMethod;
function publicMethod() {
try {
// Trigger the actual member
return method.apply(this, arguments);
}
catch (e) {
// Handle reporting the exception
reportException(e);
// Then probably re-throw it so the calling code
// sees it
throw e;
}
}
}
Bringing that all together, this code:
var Namespace = (function() {
var NS = {};
// Some setup
doSomething();
doSomethingElse();
if (/* Some condition */) {
doYetAnotherThing();
}
// Export public methods
NS.foo = foo;
NS.bar = bar;
function doSomething() {
var element = document.getElementById("foo");
// Note, next line could throw if element doesn't exist
element.addEventListener("click", function(event) {
// Handling click
var other = element.getElementsByTagName('input')[0];
element.innerHTML = other.value; // Could throw if `other` not there
}, false);
}
// ...other functions, including `foo` and `bar`...
// Return the namespace object
return NS;
})();
Turns into:
var Namespace = (function() {
var NS = {};
try {
// Some setup
doSomething();
doSomethingElse();
if (/* Some condition */) {
doYetAnotherThing();
}
// Export public methods
NS.foo = makePublic(foo);
NS.bar = makePublic(bar);
}
catch (e) {
reportException(e);
}
function doSomething() {
var element = document.getElementById("foo");
// Note, next line could throw if element doesn't exist
element.addEventListener("click", makeHandler(function(event) {
// Handling click
var other = element.getElementsByTagName('input')[0];
element.innerHTML = other.value; // Could throw if `other` not there
}), false);
}
// ...other functions, including `foo` and `bar`...
// ...`reportException`, `makeHandler`, `publicMethod`...
// Return the namespace object
return NS;
})();
So it's not that much impact.
You always want to use more targeted try/catch as part of your logic, but you can also use these global try/catch blocks to catch anything unexpected (like those silly bugs that sometimes slip into the catch block!), etc.
There are several advantages to this approach:
It works on all browsers.
You can throw things other than strings (more structured exceptions), and they'll still be
objects when you catch them.
If an error reaches the browser level, you know it's not in your code, or it's in your exception reporting code.
Use window.onerror
If for whatever reason the above isn't to your taste, a little-known feature of the browser environment is the fact that you can trap any uncaught error by assigning a function to window.onerror (live example described and linked below):
window.onerror = globalErrorHandler;
function globalErrorHandler(errorMsg, url, lineNumber) {
// Do something with the error here
}
This works in most browsers, but not all, and suffers from the fact that chaining these sorts of error handlers isn't natively supported (you have to do it yourself) and by the time the error reaches your code, it's already been turned into a string (a pain if you're using more structured exception objects).
Details on the MDC page for it, including how to play nice with others; slightly modified example:
function addWindowErrorHandler(handler) {
var previous = window.onerror;
window.onerror = function(errorMsg, url, lineNumber) {
var returnValue = false,
handled = false;
// Call the handler
try {
returnValue = handler(errorMsg, url, lineNumber);
}
catch (e) {
// Eat the error
}
// Hand off to previous
if (!returnValue && previous) {
try {
returnValue = previous(errorMsg, url, lineNumber);
}
catch (e) {
// Just eat it
}
}
// Done
return returnValue;
};
}
Just call that with a reference to your handler function, and have your handler function return true if the error was yours to handle, false otherwise.
To know whether the error is yours or not, you might consider putting a marker in the string (sadly, it'll be a string by the time it reaches the onerror handler, even if you threw some other object type). So you might use a worker function for the whole module that adds a marker, e.g.:
function myException(msg) {
return '*Marker* ' + msg;
}
Then
throw myException('cannot be negative');
and your handler would do
if (String(error).indexOf('*Marker*') >= 0) {
// It's ours
// ...handle it...
// Flag that we handled it
return true;
}
Unfortunately, even though you process the error, I'm not aware of any way to suppress it (the script still stops executing at that point).
You could even have Exception objects you construct that accept a message and a nested exception if you like. Just be sure to handle toString on them, because (again) by the time the error gets to the error handler, it's already been turned into a string.
Live example
I'd recommend a block of code that can monkey patch any function calls (nb untested):
(function (namespace) {
var addTryCatch = function (delegate) {
try {
delegate.apply(this, arguments);
} catch {
// standard code here
}
};
for (var propName in namespace) {
var prop = namespace[propName];
if (typeof prop === 'function') {
namespace[propName] = addTryCatch(namespace[propName]);
}
}
}(yourNamespace));
Any recursion could be added if necessary.
My Official answer:
<input type="button" id="btn" name="dont click me" value="dont click me" />
var _ns = { init: function() {
this.events();
},
events: function(){
$("#btn").on('click mouseover', function(event){
if(event.type != "mouseover"){
_ns.error.alert("Annnd you clicked me anyways");
}else{
_ns.error.console("nice mouseover skillz");
}
});
},
error:{
console:function (Error) {
console.log("Error: "+Error);
},
alert: function(Error){
alert("Error: "+Error);
}
}
};
$(document).ready(function() {
_ns.init();
});
After spending some time looking for the solution I came to the following conclusion. You must use window.onerror, there is no other way.