Returning the result of multiple shell commands in node.js

So what I'm trying to do is loop over an array of javascript objects (sensors), each with cmd and parser attributes, run each cmd in the system shell, parse its output with the appropriate parser function, and append the resulting string to the server's res object.

The first issue is that I get an error claiming the current object has no method parser. I've tried a few different things and still can't get it to recognize that attribute of the object.

The second issue, when I gave up and hardcoded the parse function inside the loop (which destroys my ability to add commands that need a different parser), is that because the output of each command is read in a callback function, the server runs res.end() before the callbacks return, causing an empty response to the browser.

I'm quite new to node and javascript, so I'm sure I've made some beginner mistakes, I just can't seem to sort them out in my head. Any help would be appreciated.

// requires
var http = require('http');
var exec = require('child_process').exec;

// parsers
var parseTemp = function(str) {
    return ((parseInt(str, 16) / 50 - 273.15).toFixed(2));
};

// sensors
var sensors = [
    {
        label:  "Object temp",
        cmd:    "i2cget -y 3 0x5a 0x07 w",
        parser: parseTemp,
        units:  " degrees C"
    },
    {
        label:  "Ambient temp",
        cmd:    "i2cget -y 3 0x5a 0x06 w",
        parser: parseTemp,
        units:  " degrees C"
    }
];

// server
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    for (var s in sensors) {
        exec(s.cmd, function (error, stdout, stderr) {
            res.write( s.label + ': ' + s.parser(stdout) + s.units + '\n' );
        });
    }
    res.end();
}).listen(1337, '');

console.log('Server running on port 1337');

Your first issue is a result of each callback function referencing the same s variable. You can easily solve this by changing the for(s in sensors) loop into a sensors.forEach(function(s){...}). That will close over (google javascript closure) the s and make it the right one for each callback when the time comes. There might be other problems - I didn't look all that hard once I saw this issue.

Your second issue is precisely what you said. The res.end() is being called before the callbacks - since they're asynchronous, so at best will be called after the current "thread" of execution is finished. You can solve that with a simple counter: increment before every exec call, and on each callback decrement and check if the counter has reached its initial value - if so, then run res.end().