Promises in JavaScript and NodeJS - javascript

I am retriving data from a DB. I am using promise to return the data back to the client when the data is ready:
var http = require('http');
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "******",
database: "heroes"
});
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
});
});
});
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
myPromise.then(result => res.end(result))
}).listen(8080);
I have learned that you are supposed to supply two call-back functions to the Promise and that these are optional arguments in case of success or failure.
When I am calling myResolve() (which doesn't exist in this case), the data is being sent back to the client fine using .then(). Nonetheless, when I am not calling myResolve() something doesn't work and the page is being refreshed forever.
A. If these are indeed callbacks, how can I call a non exsiting function such as myResolve() ? Why I can't extract result without calling this function ?
B. How can I execute the database query only after there is a request on the 8080 port ?
When I am trying to insert the promise into a function, I can't use .then (I am assuming the function by itself doesn't have any promise to give obiously).
Code Edit for the second question:
function connectToDB()
{
let myResult;
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
myResult = result;
});
});
});
return myResult;
}
Final code edit after wraping the promise with a function:
function connectToDB()
{
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
});
});
});
return myPromise;
}
Edit:
The questions are still valid, regardless of the use of res.send or res.end, as far as I understand it has got nothing to do with what I am asking about.

To answer these questions in order:
The function 'myResolve' does exist, as it is being passed in when you create the promise: "new Promise(function(myResolve, myReject))". In this code, you are constructing a Promise object and passing a callback function into the constructor. The two parameters in this callback function are themselves callback functions which you are naming 'myResolve' and 'myReject'. Generally, people name them 'resolve' and 'reject', but the name does not matter as long as it is consistent throughout. Functions can be passed through functions just the same as strings and other types of variables.
Right now, you are immediately declaring the 'myPromise' variable. You can defer the query until a request is hit by wrapping the 'myPromise' in a function which returns 'myPromise'. Then, when the request is made, call the new 'myPromiseFunction'.

Related

Node.js get mysql data and append to object [duplicate]

I have the following function that gets a hexcode from the database
function getColour(username, roomCount)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
return result[0].hexcode;
});
}
My problem is that I am returning the result in the callback function but the getColour function doesn't return anything. I want the getColour function to return the value of result[0].hexcode.
At the moment when I called getColour it doesn't return anything
I've tried doing something like
function getColour(username, roomCount)
{
var colour = '';
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
colour = result[0].hexcode;
});
return colour;
}
but of course the SELECT query has finished by the time return the value in colour
You have to do the processing on the results from the db query on a callback only. Just like.
function getColour(username, roomCount, callback)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err)
callback(err,null);
else
callback(null,result[0].hexcode);
});
}
//call Fn for db query with callback
getColour("yourname",4, function(err,data){
if (err) {
// error handling code goes here
console.log("ERROR : ",err);
} else {
// code to execute on data retrieval
console.log("result from db is : ",data);
}
});
If you want to use promises to avoid the so-called "callback hell" there are various approaches.
Here's an example using native promises and the standard MySQL package.
const mysql = require("mysql");
//left here for testing purposes, although there is only one colour in DB
const connection = mysql.createConnection({
host: "remotemysql.com",
user: "aKlLAqAfXH",
password: "PZKuFVGRQD",
database: "aKlLAqAfXH"
});
(async () => {
connection.connect();
const result = await getColour("username", 2);
console.log(result);
connection.end();
})();
function getColour(username, roomCount) {
return new Promise((resolve, reject) => {
connection.query(
"SELECT hexcode FROM colours WHERE precedence = ?",
[roomCount],
(err, result) => {
return err ? reject(err) : resolve(result[0].hexcode);
}
);
});
}
In async functions, you are able to use the await expression which will pause the function execution until a Promise is resolved or rejected. This way the getColour function will return a promise with the MySQL query which will pause the main function execution until the result is returned or a query error is thrown.
A similar but maybe more flexible approach might be using a promise wrapper package of the MySQL library or even a promise-based ORM.

Best practices in context of asynchronous Javascript when calling functions in functions?

I am trying to call two functions and pass the output of the first function as a parameter into the second.
Function 1:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err)
throw err;
if(user)
callback(null, user.statistics);
});
}
Function 2:
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err)
throw err;
if(statistics)
callback(null, statistics.game);
});
};
I am trying to execute the second method by passing the output of the first method as a parameter but the asynchronous nature of javascript is messing it up. I have tried implementing promises to no avail.
Can anyone suggest some good javascript practices to deal with calling functions asynchronously when they need each other? Any help would be appreciated.
After fixing the issue I mentioned above, you can call them in sequence like this:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err) callback(err);
if(user) callback(null, user.statistics);
});
};
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err) callback(err);
if(statistics) callback(null, statistics.game);
});
};
someService.getAllStatisticsByUserId(id, (err, statistics) => {
if (err || !statistics) {
// handle error
return;
}
someService.getGameByStatisticsId(statistics.id, (err, game) => {
if (err || !game) {
// handle error
return;
}
// handle game
});
});
However, as noted in Mongoose documentation:
When a callback function is not passed, an instance of Query is returned, which provides a special query builder interface.
A Query has a .then() function, and thus can be used as a promise.
So you can simply rewrite the calls like this:
someService.getAllStatisticsByUserId(id).then(statistics =>
someService.getGameByStatisticsId(statistics.id)
).then(game => {
// handle game
}).catch(err => {
// handle error
});
or convert it into an async/await function:
async function getGameByUserId(id) {
try {
const statistics = await someService.getAllStatisticsByUserId(id);
const game = await someService.getGameByStatisticsId(statistics.id);
// handle game
} catch (error) {
// handle error
}
}
Note that an async function always returns a Promise, so you must await it or chain it with a .then() to ensure completion of the query and resolve the returned value, if any.
It looks like you should be able to write:
getAllStatisticsByUserId("me", (err, stats) => {
getGameByStatisticsId(stats.id, (err, game) => {
console.log(game);
});
});
Here's how it would look if these functions returned promises instead.
getAllStatisticsByUserId("me")
.then(stats => getGameByStatisticsId(stats.id))
.then(game => console.log(game))
Even better, if you're able to use a version of Node that supports async/await then you could write.
let stats = await getAllStatisticsByUserId("me");
let game = await getGameByStatisticsId(stats.id);
console.log(game);
This would mean slightly rewriting the original functions (unless User.findById and Statistics.findById already return promises).
module.exports.getAllStatisticsByUserId = function(id, callback){
return new Promise((resolve, reject) => {
User.findById(id, (err, user) =>{
if(err) return reject(err);
return resolve(user.statistics);
});
});
}

How do we use Async, and what is the best way in nodeJS

I'm trying to pass a query to my database, then send the result to my client side, but it looks like the request is async, because my request happens after that my post request returns the value.
How do I set an await for request?
My database connection
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'my_password',
database: 'MEETME'
});
connection.connect(function(err) {
if (err) {
console.log("error while connecting to database");
console.log(err.code);
}
});
// function that query database <-------
function queryDatabase(userQuery) {
connection.query(userQuery, function(err, result) {
if (err) {
throw err;
}
console.log("before");
return result;
});
}
and this is my post request
//POST
app.post('/signin', function(request, response) {
var query = queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3");
console.log(query);
console.log("after");
response.end(query, 200);
});
the result in the console is:
undefined
after
before
Change the implementation of queryDatabase function to return a promise. Any function that returns a promise can be awaited.
function queryDatabase(userQuery){
return new Promise((resolve,reject) => {
connection.query(userQuery, function(err, result){
if(err){
reject(err);
}
console.log("before");
resolve(result);
});
});
}
app.post('/signin', async function(request, response){
var query = await queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3");
console.log(query);
console.log("after");
response.end(query, 200);
});
Welcome to Node.js where everything is intended to be asynchronous and unless you explicitly structure your code in a way that cascades one event to another there's no obligation on the part of the code to run in any particular order at all.
I'd strongly recommend picking up on Promises as a way of organizing code if you're not familiar with this concept. This wraps up a lot of tricky programming into some neat, tidy methods, and makes chaining, fan-out and fan-in actually pretty simple.
For example, rewritten with Sequelize, a database layer that uses promises:
function queryDatabase(userQuery){
console.log("before");
return connection.query(userQuery);
}
You return the promise, and that's used to chain. If you don't you must accept a callback argument and chain that through. Return values are largely ignored:
function queryDatabase(userQuery, cb){
connection.query(userQuery, function(err, result){
cb(err, result);
});
console.log("before");
}
You can see there's a lot more cruft already, and even more if you needed to build off of that. Inserting optional steps in callback driven code is tricky.
Promises make your code end up looking like this:
app.post('/signin', function(request, response){
queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3")
.then(function(results) {
console.log(results);
console.log("after");
response.end(query, 200);
});
});
It's also trivial to patch in error handling in one place with catch to handle errors.

How to return value from a function in a exported (imported through "require") module in node.js?

I have a common module that will be used by several apps. The module has a object that has several methods. I import the object through the require statement but I am having problem in understanding how I can get the returned value in an asynchronous programming model. Let me clarify it here in the following over-simplified pseudo-code:
File common_module.js:
var mysql = require('mysql');
exports.f1 = function() {
var connection_pool = mysql.createPool({host: 'xxx', user:..});
connection_pool.getConnection(function(err, connection) {
connection.query('SELECT c1 from t1 where c2 = ?', value, function(err, rows) {
value_to_return = rows[0].content; //string type
});
});
return (value_to_return);
}
The main app is, lets say, main.js:
db_getval = require('./common_module.js');
console.log(db_getval.f1());
It will always complain that value_to_return is undefined because the return statement is executed without waiting for the query to complete (thanks to asynchronous programming model). Though this is a mysql query related problem here, it will be true for many other scenarios too. How do I get around this? I am missing something basic here...
In this case, you can either use promises, or callbacks.
Callbacks in node are sort of the de-facto way of handling this kind of work:
var mysql = require('mysql');
exports.f1 = function (cb) {
var connection_pool = mysql.createPool({
host: 'xxx',
user: ..
});
connection_pool.getConnection(function (err, connection) {
if (err) {
return cb(err);
}
connection.query('SELECT c1 from t1 where c2 = ?', value, function (err, rows) {
if (err) {
return cb(err);
}
cb(null, rows[0].content);
});
});
};
// usage...
theModule(function (err, result) {
console.log(err, result);
});
That said, with the introduction of promises in node 0.12, promises are becoming far more popular due to more robust error handling. (Chaining is also very useful)
var mysql = require('mysql');
exports.f1 = function (cb) {
return new Promise(function (resolve) {
var connection_pool = mysql.createPool({
host: 'xxx',
user: ..
});
connection_pool.getConnection(function (err, connection) {
if (err) {
throw err;
}
connection.query('SELECT c1 from t1 where c2 = ?', value, function (err, rows) {
if (err) {
throw err;
}
resolve(rows[0].content);
});
});
});
};
// usage...
theModule().then(function (result) {
console.log(result);
}).catch(function (err) {
console.log(err, err.stack);
});

My callbacks are wrong - I am returning my response to my error object - Nodejs

I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})

Categories

Resources