For some reason this code gives me an uncaught exception error. It seems the catch block is not catching the error. Are try catch blocks scoped in such a way that I cannot throw an error in a nested function, and then expect it to be caught by a catch statement scoped higher up the chain? Some of the sensitive data with in the application that i'm working in has been removed, but it expected that leadInfo[ 0 / 1] would be a 32 character alpha numeric string that I pull from URL parameters.
The underlying issue here is with my AJAX call returning an error from the API and that error not being handled properly within the application. Hence the need for the throw statement. The AJAX call completes fine, and returns a JSON object that does not contain the email address as a property, so I need to handle that in a way that changes the page to reflect that.
jQuery(document).ready(function(){
try {
url = "http://api.com/api/v1/lead/" + leadInfo[1]
jQuery.ajax({
type: 'GET',
contentType: 'application/json',
url: url,
dataType : 'jsonp',
success: function (result) {
result = jQuery.parseJSON(result);
if(!result.data.email){
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
}
});
jQuery('.surveryButton').click(function(){
window.location.replace("http://" + pgInventory.host + pgInventory.path + leadInfo[0] + "&curLeadId=" + leadInfo[1] + "&curViewedPages=0");
});
}
catch(err) {
jQuery('.email').html('your e-mail address');
jQuery('#arrowContent').remove();
}
});
The reason why your try catch block is failing is because an ajax request is asynchronous. The try catch block will execute before the Ajax call and send the request itself, but the error is thrown when the result is returned, AT A LATER POINT IN TIME.
When the try catch block is executed, there is no error. When the error is thrown, there is no try catch. If you need try catch for ajax requests, always put ajax try catch blocks inside the success callback, NEVER outside of it.
Here's how you should do it:
success: function (result) {
try {
result = jQuery.parseJSON(result);
if (!result.data.email) {
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
} catch (exception) {
console.error("bla");
};
}
Due to the asynchronous nature of the callback methods in javascript, the context of the function throwing the error is different compared to the original one. You should do this way:
success: function (result) {
try {
result = jQuery.parseJSON(result);
if(!result.data.email){
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
}
catch(err) {
// Dealing with the error
}
}
I would suggest you to have a look at this excellent article about the (very particular) contexts, closures and bindings in Javascript.
The problem is that ajax is asynchronous by definition. Your exception does not get thrown from within the $.ajax function, but from the callback function on success (which is triggered at a later time).
You should give an error: function(data) {} parameter to it as well, to handle server response errors, and furthermore you should place the try/catch block inside the callback function.
If you really want to catch it outside the callback, then you should consider calling a function rather than throwing an exception, because I don't see how it can be done.
Related
I'm still learning the language, and I'm very curious to know what is the proper way to ensure that either all functions will execute or none when one action requires series of functions to be executed. For example I might have an HTML button that calls some apply() function:
function apply() {
try {
// Check arguments, choose what exactly to do next through some IFs etc...
}
anotherFunction();
}
function anotherFunction() {
try {
// Request data from DB, process data received, update object variables, etc...
}
yetAnotherFunction();
}
function yetAnotherFunction() {
try {
// Update HTML
}
oneMoreFunction();
}
function oneMoreFunction() {
try {
// Update graph
}
}
So the problem here is that if any of the functions in the flow throws an error the rest functions won't do what they should, hence the entire Apply process will be interrupted with some changes applied (let's say HTML is getting updated) but the rest (the graph) is not. I'm curious to know what is the best practice to prevent this behaviour? Yes I'm trying my best to use try {} and check arguments for errors etc, but it looks like I can't foresee everything, I just need some way to tell the code "ensure you can execute all of the functions, in case of any errors, just don't do anything at all". Please advise what can be done here?
You're taking the right path when thinking about try/catch blocks, but notice I used a 'catch' as well. Usually (maybe that is even enforced, I can't remember) you need the catch blocks along with the try.
So your functions could look something like this:
function async myFirstTryCatch() {
try {
// Make your request in the try block
await requestCall();
} catch(error){
// Hey, my http call returned an error
// Deal with the error here. Maybe show a toast, validate a form
// Anything you need to not break the code and have good UX
console.log(error)
}
}
In that same line of thought, you could have each function handle their own try/catch or maybe control that in your apply function, in case some of the chain must continue/stop depending each other.
function apply() {
try {
firstCall();
functionThatRequiresFirstCalltoSucceed();
} catch (error){
//Will catch if either firstCall or functionThatRequiresFirstCalltoSucceed fail
console.log(error)
}
functionThatIndependsFromTheResultAbove();
}
I hope this will help you build your thoughts about error handling in JS :)
IMPORTANT NOTE
If your code enters the catch block, it will consider that the error has been dealt with and will not propagate! Here's an example
function functionThatThrowsError(){
try{
throw new Error('Example Error!');
} catch (error) {
// Error has been dealt with
console.log(error) // Returns "Example Error"
// throw error; <--- Throw the error in the catch block if you need t to propagate
}
}
function wontCatchError() {
try {
functionThatThrowsError();
} catch (error) {
// THE CODE WILL NOT ENTER THE CATCH BLOCK
// SINCE THE ERROR WAS CAUGHT IN THE FUNCTION ITSELF.
// If you need to catch here as well, make sure to throw the error
// in the catch block of the 'functionThatThrowsError'
console.log(error)
}
}
I have a piece of code:
function backgroundReadFile(url, callback) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.addEventListener("load", function() {
if (req.status < 400)
callback(req.responseText);
});
req.send(null);
}
try {
backgroundReadFile("example/data.txt", function(text) {
if (text != "expected")
throw new Error("That was unexpected");
});
} catch (e) {
console.log("Hello from the catch block");
}
In my console I get
Error: That was unexpected (line 13)
which is just fine. But, I am told that:
In the code, the exception will not be caught because the
call to backgroundReadFile returns immediately. Control then leaves
the try block, and the function it was given won’t be called until
later.
The question is: why other errors will not be caught here? When we have, say, connection problems, or the file does not exist? As far as I can see, the callback function won`t execute if
req.addEventListener("load")
is not triggered, for example. But it still does - I still get the same error - Error: That was unexpected (line 13).
What does it mean - "exception will not be caught because the call to backgroundReadFile returns immediately"?
Thank you.
Here's a step-by-step breakdown of what happens in your code.
The code enters the try-catch block.
backgroundReadFile is called, with two parameters: "example/data.txt", and an anonymous function.
backgroundReadFile creates an AJAX request and calls send(). Here's where the concept of asynchrony comes into play: the actual HTTP request is not sent right away, but rather placed in a queue to be executed as soon as the browser has finished running whatever code it is running at the moment (i.e. your try-ctach block).
backgroundReadFile has thus finished. Execution returns to the try-catch block.
No exceptions were encountered, so the catch block is skipped.
The code containing the try-catch block has finished execution. Now the browser can proceed to execute the first asynchronous operation in the queue, which is your AJAX request.
The HTTP request is sent, and once a response is received, the onload event handler is triggered -- regardless of what the response was (i.e. success or error).
The anonymous function you passed to backgroundReadFile is called as part of the onload event handler, and throws an Error. However, as you can see now, your code is not inside the try-catch block any more, so it's not caught.
TL;DR: The function that throws the Error is defined inside the try-catch block, but executed outside it.
Also, error handling in AJAX requests has two sides: connection errors and server-side errors. Connection errors can be a request timeout or some other random error that may occur while sending the request; these can be handled in the ontimeout and onerror event handlers, respectively. However, if the HTTP request makes it to the server, and a response is received, then as far as the XMLHttpRequest is concerned, the request was successful. It's up to you to check, for example, the status property of the XMLHttpRequest (which contains the HTTP response code, e.g. 200 for "OK", 404 for "not found", etc.), and decide if it counts as successful or not.
Your backgroundReadFile function has two parts: A synchronous part, and an asynchronous part:
function backgroundReadFile(url, callback) {
var req = new XMLHttpRequest(); // Synchronous
req.open("GET", url, true); // Synchronous
req.addEventListener("load", function() { // Synchronous
if (req.status < 400) // *A*synchronous
callback(req.responseText); // *A*synchronous
});
req.send(null); // Synchronous
}
You're quite right that an error thrown by the synchronous part of that function would be caught by your try/catch around the call to it.
An error in the asynchronous part will not by caught by that try/catch, because as someone told you, by then the flow of control has already moved on.
So it could be perfectly reasonable to have a try/catch around the call to that function, if it may throw from its synchronous code.
Side note: If you're going to use the callback style, you should always call back so the code using your function knows the process has completed. One style of doing that is to pass an err argument to the callback as the first argument, using null if there was no error, and then any data as a second argument (this is often called the "Node.js callback style"). But in modern environments, a better option would be to use a Promise. You can do that with minimal changes like this:
function backgroundReadFile(url) {
return new Promsie(function(resolve, reject) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.addEventListener("load", function() {
if (req.status < 400) {
resolve(req.responseText);
} else {
reject(new Error({status: req.status}));
});
req.addEventListener("error", reject);
req.send(null);
});
}
...which you use like this:
backgroundReadFile(url)
.then(function(text) {
// Use the text
})
.catch(function(err) {
// Handle error
});
But in the specific case of XMLHttpRequest, you could use fetch instead, which already provides you with a promise:
function backgroundReadFile(url) {
return fetch(url).then(response => {
if (!response.ok) {
throw new Error({status: response.status});
}
return response.text();
});
}
The following script enables you to run a piece of javascript code. Errors are being catched by the try / catch block.
try {
var result = eval(script);
} catch (e) {
// do something meaningful
}
However, if the variable script contains for instance an AJAX call, and this ajax call throws an exception (e.g. in the success function), this exception will NOT be catched by this try / catch block...
// execute an AJAX request
var script = '$.ajax(url:"/somewhere", success: function(){throw new MyException('testexception')})';
try {
var result = eval(script);
} catch (e) {
// will not be triggered...
}
Question: how can I catch the exception thrown within the ajax request?
I hope you are aware of the dangers of using eval, and if not there are plenty of good articles out there that explain why it is not a good idea.
That being said, the issue is that the success callback is being called after the catch block. You'll need to either add the try/catch block within the success callback, or you'll need to handle the error from a more global perspective. One idea I can think of to do this is using the window.onerror event. I have an example below that shows something similar to your problem, and one that shows you can catch errors thrown in eval.
(function() {
'use strict';
window.addEventListener('error', e => console.log(`window.onerror: ${e.message}`));
let script = `setTimeout(function() {
throw new Error('Whoops!');
}, 0);`;
eval(script);
script = `throw new Error('Whoops!');`;
try {
eval(script);
} catch (e) {
console.log(e.message);
}
})();
I'm trying to request the data from server using dojo jsonrest store. While requesting i'm catching the callback to do some stuff. For example
this.myStore.get(paramValue).then(lang.hitch(this, function (res) {
//do something like this.globalVal = res;
}, function (err) {
console.log(err);
//throw error
}));
But the above code only works when the request returns success, i.e. it dose enter in the first block of deferred on success return, but when some error occurred, it failed to reach in the error callback and hence i'm not able to catch the errors returned by server.
If i do the above code without using lang.hitch like this
this.myStore.get(paramValue).then(function (res) {
//do something like this.globalVal = res;
}, function (err) {
console.log(err);
//throw error
});
Then it works. i.e. it will now reach to the error callback as well and i can throw the appropriate error to user.
So why dose this happen, and if lang.hitch is not something that can be used with deferred then what to use?
Thanks
Hitch accepts two arguments, context and the function which is to be executed in the preceding context. At the moment you're using three, that won't work. You're trying to wrap two functions into the same hitch. You'll need to wrap them each in a separate hitch:
this.myStore.get(paramValue).then(
lang.hitch(this, function success (res) {
console.log('Success');
}),
lang.hitch(this, function error (err) {
console.log('Error');
})
);
Try catch is used to catch errors and report to user. all said and fine. But where exactly one has to put the try-catch. Or What exactly has to go inside try catch on a usual basis.
Most importantly, is it absolutely a good coding practice to have a try catch block?
I think it is good practce to use a try catch if it will handle errors and prevent the program from crashing.
Taken from W3 Schools:
The try statement lets you test a block of code for errors.
The catch statement lets you handle the error.
The throw statement lets you create custom errors.
The finally statement lets you execute
code, after try and catch, regardless of the result.
An example:
fuction foo()
{
try
{
// Block of code to try
}
catch(e)
{
// Block of code to handle errors
document.getElementById("demo").innerHTML = e.message;
}
finally
{
// Block of code to be executed regardless of the try / catch result
}
}
Here is soem more documentation at W3 Schools: http://www.w3schools.com/js/js_errors.asp
Yes, it absolutely good practice to use try-catch blocks. Here's a rather simplistic (but contrived) demonstration.
function safeParseJSON(json) {
try {
return JSON.parse(json);
} catch(exception) {
// could not parse this JSON
console.error(exception);
} finally {
return null;
}
}
Parsing JSON is the most common scenario I have encountered for using the try-catch construct, hence the example.
However, the usual try-catch mechanism doesn't work when the function is asynchronous. It's essential to understand this if you are ever using a server side Javascript platform.
The standard pattern for async event handling is as follows.
db.connect(options, function connected(err, client) {
if(err) throw err;
client.query(...);
});
Because that callback function is run somewhere else (presumably when the database connects) we can't wrap it with a try-catch block. Instead, most async method calls will pass the error as the first argument to the callback.
This way we can handle the error as and when it happens. If there is no error, null will be passed as the first argument, so that it can be ignored.
A number of implementations of promises try to recreate this mechanism, but in an asynchronous way.
Here's an example with Q:
db.connect(options)
.then(function(client) {
client.query(...);
})
.catch(function (error) {
throw error;
})
.fin(function() {
// finally
db.close();
});
You can also use Q on the client side, if you are working with asynchronous functions.