Vadorequest Vadorequest - 27 days ago 13
Javascript Question

Node.js return, callback and source code execution flow - Runtime

I'm pretty confused by the way to work on node.js, I'm talking about callbacks, returns and how source code is executed.

I'm using sails.js but I don't think it's linked, it's more the way to JS works, I think.

Source code:

module.exports = function (req, res, callback) {
if (req.session.authenticated) {
// Accept user authentication.
return callback();
} else {
// Not authenticated. Try auto authentication.
if(validator.check([validator.rulesHardwareId(req.param('hardwareId'))])){
Device.findOne({hardwareId: req.param('hardwareId')}, function(err, device){
if(err){
return res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Wrong hardwareId or DB error]", data: {err: err}, status: false}, 403);
}

if(device){
// Connect the device.
req.session.authenticated = true;
req.session.from = 'device';

// Search for the device's owner.
User.findOne({id: device.userId}, function(err, user){
if(err){
return res.json({message: "DB error.", data: {err: err}, status: false}, 403);
}

if(user){
// Add data about the user.
req.session.user = user;
return callback();
}else{
return res.json({message: "Device found but device's owner doesn't found.", data: {err: err}, status: false}, 403);
}
});
}else{
return res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Wrong hardwareId]", data: {err: err}, status: false}, 403);
}
});
}
return res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Give hardwareId ?]", data: {}, status: false}, 403);

}
};


The code is not so important, the thing is that I received this message:
"You are not permitted to perform this action. You have to connect to the platform before. [Give hardwareId ?]"

But the action is CREATED. Okay, so I call the callback() and I return it, but the source code continue? And execute the last line? Why? I don't understand that.
If I put the last line in a ELSE, I receive the message "The action is created".

If someone could explain me.. I thought that add the return keyword was useful for prevent this, but it looks like I'm wrong.

Thank you.

Answer

When you make asynchronous requests like your code does, the flow of execution does not wait for the response. It just continues along as though there was nothing there to prevent it (because there is nothing there). So your main function returns immediately.

The nested return statements you've provided are returning a value to the caller of those nested functions. So who is the caller? Well, you're passing those functions as an argument to some internal code, so the internal code is the caller, and that internal code probably doesn't care about the return value.

So what this means is that all those return statements are doing absolutely nothing useful because your main function already returned, and the code calling your functions is ignoring them.

So what do you do? Well, as you can see, you're receiving a callback function that gets passed to your module.exports function. So what you should do is pass the data that you would normally return as an argument to the callback. Then whatever function was passed as the callback will receive that data, and do whatever you want with it.

module.exports = function (req, res, callback) {
    if (req.session.authenticated) {
        // Accept user authentication.
        callback();
    } else {
        // Not authenticated. Try auto authentication.
        if(validator.check([validator.rulesHardwareId(req.param('hardwareId'))])){
            Device.findOne({hardwareId: req.param('hardwareId')}, function(err, device){
                if (err) {
                    callback(res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Wrong hardwareId or DB error]", data: {err: err}, status: false}, 403));
                } else if (device) {
                    // Connect the device.
                    req.session.authenticated = true;
                    req.session.from = 'device';

                    // Search for the device's owner.
                    User.findOne({id: device.userId}, function(err, user){
                        if (err) {
                            callback(res.json({message: "DB error.", data: {err: err}, status: false}, 403));
                        } else if (user) {
                            // Add data about the user.
                            req.session.user = user;
                            callback();
                        } else {
                            callback(res.json({message: "Device found but device's owner doesn't found.", data: {err: err}, status: false}, 403));
                        }
                    });
                } else {
                    callback(res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Wrong hardwareId]", data: {err: err}, status: false}, 403));
                }
            });
        } else {
            callback(res.json({message: "You are not permitted to perform this action. You have to connect to the platform before. [Give hardwareId ?]", data: {}, status: false}, 403));
        }
    }
};

So your function would be invoked something like this:

 // This is the callback ------v
my_module(the_req, the_res, function(err) {
    if (err) {
        // do something with the error
        console.log(err);
    }
});
Comments