Eternyt
Posts: 38
Joined: Tue Dec 19, 2017 4:23 pm

Variable as 0 when returned NodeJS

Wed Aug 01, 2018 9:41 am

Hi,

I have this function at the bottom of my code outside every other function:

Code: Select all

function getTemp() {
    db.get("SELECT temperature FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
        console.log("Database response " + row.temperature);
        return row.temperature;
    });
}
And I need to retrieve the returned valued inside a server.post callback, inside a switch:

Code: Select all

case "temperatura":
            let temp = getTemp();
            console.log(temp);
            fulfillmentText = "La temperatura esterna è pari a " + temp + "gradi";
            break;
But the temp value is always displayed as 0.
I also read online that my problem is probably not regarding the impossibility of reading the variable but the fact that while NodeJS is opening the database it has already executed the "FulfillmentText" line.

Any advice on how to write it properly?
Thanks!

(the entire code for better understanding):

Code: Select all

var express = require('express');
var http = require('http');
var https = require('https');
var fs = require('fs');
var bodyParser = require("body-parser");

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(*privacy*, sqlite3.OPEN_READONLY);


var server = express();

// Enable JSON encoding
server.use(
    bodyParser.urlencoded({
        extended: true
    })
);
server.use(bodyParser.json());

server.get('/', function (req, res) {
    console.log("GET request");
    res.send("Hello World!");
});

server.post('/', function(req, res) {
    res.setHeader('Content-Type', 'application/json');
    console.log("POST request");
    var fulfillmentText = "";
    switch (req.body.queryResult.parameters.meteo) {
        case "temperatura":
            let temp = getTemp();
            console.log(temp);
            fulfillmentText = "La temperatura esterna è pari a " + temp + "gradi";
            break;
        case "umidità":
            let temp = getTemp();
            fulfillmentText = "L'umidità esterna è pari al " + hum + "percento";
            break;
        default:
            fulfillmentText = "Non ho riconosciuto una mazza";
            break;
    }
    return res.json({
      fulfillmentText: fulfillmentText
    });
});

https.createServer(*privacy*, server).listen(8080);


function getTemp() {
    db.get("SELECT temperature FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
        console.log("Database response " + row.temperature);
        return row.temperature;
    });
}

bertlea
Posts: 169
Joined: Wed Dec 07, 2016 6:33 am
Location: Hong Kong

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 2:00 am

The reason you do not get the value as you expected is simply that the db.get() function only offer you the temperature via the asynchronous function you passed in as the last parameter. Hence, immediately after the call ‘let temp = getTemp();’ the variable temp is just assigned a value returned by db.get, not the return value of the callback function you passed in. I think that just gives you 0.

You need to think and program it as an asynchronous event driven system. You can use the traditional ‘callback hell’ method. Or for ES6, you can use ‘promise’. Or even newer async/await to handle asynchronous callback functions. Actually, if you are using Node.js you should already familiar of using asynchronous callbacks as most functions in Node.js such as reading and writing to a text file also use asynchronous callbacks unless you explicitly use the “synchronous” version of the function for simplicity at the cost of blocking the Node.js process.

Eternyt
Posts: 38
Joined: Tue Dec 19, 2017 4:23 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 9:20 am

Thank you! I thought it was that the problem.
Now, whats the best way rewrite it and made it actually work?

Thanks again!

bertlea
Posts: 169
Joined: Wed Dec 07, 2016 6:33 am
Location: Hong Kong

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 9:56 am

For simple implementation, can you just try to put the res.json response inside your callback function? I modified some of your code as below. But I did not test it at all. Not sure if it got errors, but you can see the concept I guess. Also, this may not the best way to do it too. You should try to use promise or async/await to do that.

Edited half hour later to change some code....

Code: Select all

server.post('/', function(req, res) {
    res.setHeader('Content-Type', 'application/json');
    console.log("POST request");
    var fulfillmentText = "";
    switch (req.body.queryResult.parameters.meteo) {
        case "temperatura":
			    db.get("SELECT temperature FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
				    console.log("Database response " + row.temperature);
				    return res.json({fulfillmentText: "La temperatura esterna è pari a " + row.temperature + "gradi"});
                });
            break;
        case "umidità":
//            let temp = getTemp();
//            fulfillmentText = "L'umidità esterna è pari al " + hum + "percento";
            break;
        default:
		    return res.json({fulfillmentText: "Non ho riconosciuto una mazza"});
//    		fulfillmentText = "Non ho riconosciuto una mazza";
//            break;
    }
});

https.createServer(*privacy*, server).listen(8080);

Eternyt
Posts: 38
Joined: Tue Dec 19, 2017 4:23 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 12:40 pm

Tried the code and I started to understand the logic.
But NodeJS is throwing the error that "res" in getHum() is not defined and I have no idea on how to make it global :(

Heater
Posts: 9841
Joined: Tue Jul 17, 2012 3:02 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 2:07 pm

The thing to understand here is that when you call db.get() it will NOT return immediately. It does not hang up for ages waiting for a result from the database query. Typically a database in on the end of a network connection and take some time, so not waiting means that your server can continue doing other work whilst it is waiting.

Eventually the database query returns a result, at which point the function that you passed as the second parameter is called. That function is given the result and error status as parameters. Inside that function you can finish the HTTP query by calling res.json() or whatever. Always check the error parameter.

You have an extra layer of indirection here as you have put the db.query inside another function, getTemp(). That's OK, just give getTemp a function to call when it has the result. Inside that function you can call res.json().

Here is a working example I created from your code:

Code: Select all

const express = require('express')
const http = require('http')
const fs = require('fs')
const bodyParser = require("body-parser")

const sqlite3 = require('sqlite3').verbose()
const db = new sqlite3.Database(':memory:', sqlite3.OPEN_READONLY)

const server = express();

// Enable JSON encoding
server.use(
  bodyParser.urlencoded({
    extended: true
  })
);
server.use(bodyParser.json())

server.get('/temp', function(req, res) {
  // This anonymous function is called when a request to /temp is made
  res.setHeader('Content-Type', 'application/json')
  console.log("GET request")
  getTemp(function (temp) {
    // This anonymous function is called when a getTemp has fetched the temperature
    console.log("The temp is:", temp)
    if (temp) {
      res.json({temperature: temp})
    } else {
      res.json({temperature: "UNDEFINED"})
    }
  })
})

http.createServer(server).listen(8080);

function getTemp(cb) {
  db.get("SELECT temperature FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
    // This annonymous is called when the db.get has received a result (or error) from the database
    if (err) {
      // Always check for errors
      console.log('db.get: failed: ', err)
      // Call the given call back function with no result
      cb(undefined);
      return
    }
    console.log("Database response " + row[0].temperature)
      // Call the given call back function with the received temperature
      cb(row[0].temperature)
  })
}
Do check up on the event driven nature of Javascript and the way call backs are used in node.js.

Some like to call this "callback hell'. Well it can be in complicated situations but generally, as in your example, it is not any problem. Far better than "thread hell" that you might get into in other languages to achieve the same effect and far more efficient.

Heater
Posts: 9841
Joined: Tue Jul 17, 2012 3:02 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 2:14 pm

Eternyt,

It's no good talking about "getHum" we have never seen it in your example code.

Do not make things global unless you really have to. You can always pass "res" or whatever as a parameter to this getHum() function.

Don't forget that a function that is defined inside another function can see all the local variables and parameters of that enclosing function. See "javascript closure".

Personally I would not do that. I would keep web server stuff like req and res separate from the database stuff like db.get(). To that end pass a callback function to getTemp and such db query functions, as I show above.

Eternyt
Posts: 38
Joined: Tue Dec 19, 2017 4:23 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 2:15 pm

Many thanks for all the response!

Ended up with a very similar code:

Code: Select all

var express = require('express');
var http = require('http');
var https = require('https');
var fs = require('fs');
var bodyParser = require("body-parser");

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('*sensitive data*', sqlite3.OPEN_READONLY);


var server = express();

// Enable JSON encoding
server.use(
    bodyParser.urlencoded({
        extended: true
    })
);
server.use(bodyParser.json());

server.get('/', function (req, res) {
    console.log("GET request");
    res.send("Hello World!");
});

server.post('/', function(req, res) {
    res.setHeader('Content-Type', 'application/json');
    console.log("POST request");
    var fulfillmentText = "";
    switch (req.body.queryResult.parameters.meteo) {
        case "temperatura":
            getTemp(function(temp) {
                console.log("Variable inside my Temp callback: " + temp);
                fulfillmentText = "La temperatura esterna è pari a " + temp + " gradi";
                return res.json({
                    fulfillmentText: fulfillmentText
                });
            });
            break;
        case "umidità":
            getHum(function(hum) {
                console.log("Variable inside my Hum callback: " + hum);
                fulfillmentText = "L'umidità esterna è pari al " + hum + " percento";
                return res.json({
                    fulfillmentText: fulfillmentText
                  });
            });
            break;
        default:
            fulfillmentText = "Non ho riconosciuto una mazza";
            return res.json({
                fulfillmentText: fulfillmentText
              });
    }
    
    
});

var sslOptions = {
	*sensitive data*
    };

https.createServer(sslOptions, server).listen(8080);


function getTemp(callback) {
    db.get("SELECT temperature FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
        console.log("Database response " + row.temperature);
        callback(row.temperature);
    });
}

function getHum(callback) {
    db.get("SELECT humidity FROM valuex ORDER BY dateID DESC LIMIT 1", function(err, row) {
        console.log("Database response " + row.humidity);
        callback(row.humidity);
    });
}

Heater
Posts: 9841
Joined: Tue Jul 17, 2012 3:02 pm

Re: Variable as 0 when returned NodeJS

Thu Aug 02, 2018 3:16 pm

Looks good.

Don't forget to check all those "err" parameters to callback functions.

Eternyt
Posts: 38
Joined: Tue Dec 19, 2017 4:23 pm

Re: Variable as 0 when returned NodeJS

Fri Aug 03, 2018 10:16 am

Ok!

Many thanks!

Return to “Other programming languages”

Who is online

Users browsing this forum: No registered users and 3 guests