faboolous faboolous - 4 months ago 11x
Node.js Question

mongodb crashed node with exception within try catch

p = req.params.name
Item.update('name': p, req.body , {upsert: true}, (err) ->
if err?
throw err
catch e
handle_error(e, "Error salvando hoja de vida.", res)

This produces an error in my code right now - that's alright, but why does my nodejs program crash even if I have a try catch here?

The error is:

MongoError: Mod on _id not allowed

(so it must be in the update call)
I am specifically looking for a way to catch an error, I already know how to get rid of it.


Ah yes, you have entered the realm of asynchronous code. The reason you pass a callback into your MongoDB calls like Item.update(..., callback) is because the MongoDB driver is written to be asynchronous. Consider this (mongoose code):

var user = User.findOne({ name: 'joe' });

If it had structured its API like the above code then your entire application would halt until User.findOne() returned the results from the database. Your doSomethingElse() call wouldn't be invoked until the user was retrieved, even if you don't do anything with user inside doSomethingElse. This is a big no no these days and especially in node which has been written to be asynchronous as much as possible. Have a look at the following:

User.findOne({ name: 'joe' }, function (err, user) {
    if (err) return console.error(err);
    console.log('Do stuff with user... ;)');

The above code is asynchronous. The findOne function returns immediately and doSomethingElse is able to begin even before the user is ever retrieved from the database. But of course we still want to do stuff with our user, so in order to accomplish this we pass an anonymous function in to be used as a callback. The MongoDB driver is smart enough to call that function when it's all done retrieving data.

Wrapping the above code in a try/catch would be pointless, unless you suspected the findOne function to throw an exception (Which you shouldn't. It returns immediately remember?).

try {
    User.findOne({ name: 'joe' }, function (err, user) {
        throw new Error("Something went wrong...");
} catch (err) {

The above error would still crash your program because it happened long after findOne returned and your program moved on way beyond that precious try/catch. This is the very reason why your callback function receives an err argument. The MongoDB driver knows that if some error occurs while fetching your data it would be no good to throw an exception and call it good. This is because all this processing has happened later on subsequent trips through the event loop, having left your try/catch behind several iterations ago.

To get around this the MongoDB driver wraps its own internal asynchronous code in a try/catch where it actually will handle the exception and then it invokes your callback passing in the error as the first argument. This allows you to check that argument and handle any errors within your callback.

I wrote an entire blog post explaining callbacks, as well as another way of handling asynchronous code called "promises" and I think it would help clarify some things for you :) http://codetunnel.io/what-are-callbacks-and-promises

I hope that helps answer your question.