Throwing and Catching
Just a warning: I'm going to delve a bit into Javascript with try
, throw
, and catch
. If you already know how these work please feel free to skip to the next section.
A crucial, and sometimes rather tedious aspect of creating apps is making sure you account for every scenario. What if we run the method with dodgy parameters? Does it return a helpful error? To test this we can run the method with dodgy fake _id
s, but this time time we will console.log
the error
callback parameter instead of response
. Try running this in the browser console:
Meteor.call("gamesInsert", "hello", "goodbye", function(error, response){
console.log('error', error);
console.log('response', response);
});
You will find two errors will pop up. The first is an exception caused by the dodgy parameters, which is useful to developers but won't help the users. The second is the result of our console.log(error)
. This is the object we have to use to handle the error on the client.
Now we could easily do a kind of catch all solution, such as presenting an alert box and displaying a generic message:
Meteor.call("gamesInsert", "hello", "goodbye", function(error, response){
if(error){
alert("Something went wrong");
}
});
But what if we wanted to be more specific? What if we wanted to say "One of these teams doesn't exist in the database."?
In Javascript we have the ability to "throw" errors. And if we "throw" an error we can also "catch" it. You can run this simple example in the browser console:
try {
throw "hi"
} catch (error) {
console.log(error);
}
It should print out "hi". We need to wrap the throw
in a try
block so Javascript knows we are trying to "catch" errors. If we just went throw "hi"
without the try
it gives the error "Uncought hi":
While we can throw
anything as an error, it's better to throw something that gives a bit more information than just a string. But what should we throw?
Perhaps we should look at what the browser throws when we make an error in our code:
Here we see the browser throws a ReferenceError
object, which inherits its properties from Error
. There are several different error objects the browser can throw, and they all inherit from Error
. But they all have the same attributes - a name and a message. The only difference between ReferenceError
and Error
is that they have different name values ("ReferenceError" and "Error").
We can set the message like this:
throw new Error("Message");
// the error will have the name "Error" and the message "Message"
throw new ReferenceError("Message");
// the error will have the name "ReferenceError" and the message "Message"
Looking back at our Meteor method. Let's try throwing an error when we can't find one of the teams:
both/collections/games.js
Meteor.methods({
...
gamesInsert: function(teamOneId, teamTwoId){
var teamOne = Teams.findOne({_id: teamOneId});
var teamTwo = Teams.findOne({_id: teamTwoId});
if(!teamOne || !teamTwo){
throw new Error("One of the teams doesn't exist in the database");
}
var teamOneData = {
_id: teamOne._id,
name: teamOne.name,
score: 0
};
var teamTwoData = {
_id: teamTwo._id,
name: teamTwo.name,
score: 0
};
var game = {
ownerId: Meteor.userId(),
createdAt: new Date(),
teams: [teamOneData, teamTwoData],
completed: false
}
var gameId = Games.insert(game);
// Update each team's cached array of game ids
Teams.update({_id: teamOneData.id}, {$addToSet: { gameIds: gameId}});
Teams.update({_id: teamTwoData.id}, {$addToSet: { gameIds: gameId}});
// Copy Meteor.insert(), which just returns the _id
return gameId;
}
});
Now run the method in the browser console again:
Meteor.call("gamesInsert", "hello", "goodbye", function(error, response){
console.log('error', error);
console.log('response', response);
});
Upon running this we see something is still not right. Our Error
is outputted by the browser, but the output of console.log(error)
- the one we care about - is still the same as last time: