Paul Paku Paul Paku - 1 month ago 10
Node.js Question

node's module function return value empty/undefined?

I'm trying to get the html encoded table row value, returned from the slqLite based logger. As I'm new to node modules I'm stuck at:

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');
var html = '';

module.exports = {
readHtml: function() {
var html = ''; // optional but does not work here as well
db.serialize(function() {
db.each("SELECT rowid AS id, info FROM logger", function(err, row) {
html = html + '<tr><td>' + row.info + '<td><tr>'; << html is growing
console.log('Log: ' + row.info); << working
});
});
console.log(html); // html gets empty here!
return html;
}
}


So have no value returned from:

var sysLog = require('logger');

sysLog.init();
sysLog.write('test string1');
sysLog.write('test string2');
console.log(sysLog.readHtml());


It has to be very simple to be solved ...
node is 6.7

Answer

You problem is directly related to a very common issue when starting with JavaScript:

How do I return the response from an asynchronous call?

Which shows the simplest way to receive results of an asynchronous operation, such as db.each is using a callback.

function readHtml()
  var html = ''
  db.serialize(function() {
    db.each(..., function(err, row) {
      // this line executes sometime later
      // after we already returned from readHtml()
    });
  });
  // this line executes right after the call to db.serialize
  // but before db.each calls the callback we give to it.
  // At this point, html is '' because we still didn't read any rows 
  // (they are read asynchronously, sometime later)
  return html;
}

readHtml(); // <-- this is always '' because rows are read at a later point

To solve this, you would need a function that will be called with a callback like this:

readHtml(function(html) { // <-- this callback gets called once all rows are read
  console.log(html);
});

Your situation also has an additional complication that db.each calls its callback once for every row. By looking at the docs, you can see that db.each accepts an additional complete callback when all rows are read. You can use this callback to signalize reading is done and pass the html results.

Here's how you can define readHtml:

function readHtml(callback) { // pass in a callback to call once all rows are read and all html is accumulated
  var html = '';
  db.serialize(function() {
    // read all the rows and accumulate html as before
    db.each("SELECT rowid AS id, info FROM logger", function(err, row) {
      html = html + '<tr><td>' + row.info + '<td><tr>';
    }, function() { 
      callback(html); // use the second callback to signal you are done and pass the html back
    });
  });
}
Comments