I recently have been developing a web app with NODEJS. I have a db model that takes care of all crud operations. Sample DB model
getDeviceList: function () {
return new Promise(function (resolve, reject) {
var sql = "SELECT * FROM `devices`";
db.query(sql, function (err, result) {
if (err) {
console.log(`FAILED: ${err}`);
reject(err);
} else resolve(result);
});
});
}
And I access this function in DB module like
router.get("/inventory/devices", checkAuth, function (req, res, next) {
return new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(res.status(200).json(result));
}).catch(function (err) {
console.log(`FAILED: ${err}`);
reject(res.status(200).json(err));
});
});
});
Sometimes when I close the DataBase; I get UnhandledPromiseRejectionWarning but In my eyes I am handling all the promises. What is the best way of handling promises and is my method of retrun promise correct?
My DB connection block is in app.js
var connection = db.connectDB().then(function (connect) {}).catch(function (err) {
console.log(`FAILED: ${err}`);
});
and my connectDB() is
function () {
return new Promise(function (resolve, reject) {
db = mysql.createPool({
host: "localhost",
port: 3306,
user: "migration",
password: "iub5015",
database: "migration"
});
db.getConnection(function (err) {
if (err) {
console.log(`FAILED: ${err}`);
reject(err);
} else {
console.log("Connected");
resolve(db);
}
});
});
}
The last statement in your catch block,
reject(res.status(200).json(err));
means that the returned promise is in rejected state. ie the Promise returned by router.get("/inventory/devices", checkAuth, function (req, res, next) is in rejected state and you have missed to catch that. (or express does not catch that as it seems in your case)
This is why you are getting this UnhandledPromiseRejectionWarning.
What is the best way of handling promises and is my method of retrun
promise correct?
Well, in my experience it depends. In some scenarios you may want to propagate error by rejecting Promise, or in some case you may just want to handle it some way and return something as resolved value. In your case you may have to look into express docs to figure out how you need to implement promises within your middleware.
router.get("/inventory/devices", checkAuth, function (req, res, next) {
return new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(res.status(200).json(result));
}).catch(function (err) {
console.log(`FAILED: ${err}`);
// This will not give UnhandledPromiseRejectionWarning
resolve(res.status(500).json(err));
});
});
});
router.get("/inventory/devices", checkAuth, function (req, res, next) {
new Promise(function (resolve, reject) {
db.getDeviceList().then(function (result) {
resolve(result);
}).catch(function (err) {
console.log(`FAILED: ${err}`);
reject(err);
});
}).then(r => {
res.status(200).json(r)
}).catch(er => {
res.status(400).json(er)
})
});
Related
I have already a function written with bluebird promises and I would like to rewrite it with async and await. When I have made the changes I have found out that earlier with promises the reject statement always transfers the control to called function catch block though if the catch block is already there in the file from where we are rejecting. How to handle this situation properly with async and await?. (Added comments to the code to explain the issue)
With Promise:
const callingFunc = (req, res) => {
return new Promise((resolve, reject) => {
// execute request which returns promise
functionCall()
.then((response) => {
let error;
try {
xml2js(response.body, { explicitArray: false }, (err, result) => {
if (err) {
return reject(err); /* throws the correct error to catch block of the file from where callingFunc is called*/
}
if (!_.isEmpty(result.Response.errorCode)) {
return reject(result.Response); /* throws the correct error to the catch block of the file from where callingFunc is called*/
}
return resolve(result);
});
} catch (e) {
error = new Error('xml2js conversion error');
reject(error);
}
})
.catch((error) => {
const Error = new Error('Internal Server Error');
reject(Error);
});
});
};
With async and await:
const callingFunc = (req, res) => {
try {
const response = await functionCall();
let error;
try {
xml2js(response.body, { explicitArray: false }, (err, result) => {
if (err) {
throw (err); /* throws the error to the below catch block and returning xml2js conversion error and changing behaviour*/
}
if (!_.isEmpty(result.Response.errorCode)) {
throw result.Response; /* throws the error to the below catch block and returning xml2js conversion error and changing behaviour*/
}
return result;
});
} catch (e) {
error = new Error('xml2js conversion error');
throw error;
}
} catch(error) {
const Error = new Error('Internal Server Error');
throw Error;
}
};
If functionCall returns a promise, then this code is inappropriate...
return new Promise((resolve, reject) => {
// execute request which returns promise
functionCall()
.then((response) => {
If xml2js is async using callbacks, then it is appropriate to wrap it in a promise...
// return a promise that resolves with the result of xml2js
async function xml2js_promise(body) {
return new Promise((resolve, reject) => {
xml2js(body, { explicitArray: false }, (err, result) => {
if (err) reject(err);
else if (!_.isEmpty(result.Response.errorCode)) reject(result.Response);
else resolve(result);
});
});
}
Now we can await these. There's no need to nest the try's. (And you only need the try if you're going to do something on the catch).
async callingFunction = (req, res) => {
try {
const response = await functionCall();
} catch (error) {
// do something with this error
}
try {
const result = await xml2js_promise(response.body)
} catch(error) {
// do something with this error
}
return result;
}
I'm trying to rewrite my code to incorporate promises. I know that mongo already incorporates promises but i'd like to understand promises a bit more first. I don't understand the error message because I have used await in the async function. I found this articles that seems to do it similarly, but I still wasn't able to get it working.
What am i doing incorrectly here?
error message
SyntaxError: await is only valid in async function
code
app.post('/search/word',urlencodedParser, async function(req, res){
try{
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
let dbo = db.db("words");
//Declare promise
let searchWord = function(){
return new Promise(function(resolve, reject){
dbo.collection("word").find({"$text": {"$search": req.body.word}})
.toArray(function(err, result) {
err ? reject(err) : resolve(result);
});
});
};
result = await searchWord();
db.close();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result));
});
} catch(e) {
console.log(e);
}
});
The callback functions needs to be async
app.post('/search/word',urlencodedParser, async function(req, res){
try{
MongoClient.connect(url, { useNewUrlParser: true }, async function(err, db) {
if (err) throw err;
let dbo = db.db("words");
//Declare promise
let searchWord = function(){
return new Promise(function(resolve, reject){
dbo.collection("word").find({"$text": {"$search": req.body.word}})
.toArray(function(err, result) {
err ? reject(err) : resolve(result);
});
});
};
result = await searchWord();
db.close();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result));
});
} catch(e) {
console.log(e);
}
});
I am trying to show results of two SQL queries in one page. My code is in module.exports block and the whole application in written in Node.js Express. This returns an error "Cannot read property 'length' of undefined".
var message = "some random text";
var res_points, res_types;
db.query(query, (err, result) => {
if (err) {
res.redirect('/');
}
res_points= result;
});
db.query(query2, (err, result) => {
if(err) {
res.redirect('/');
}
res_types = result;
});
res.render('index.ejs', {
title: message,
,points: res_points
,types: res_types
});
When I do it like this, it works:
db.query(query, (err, result) => {
if (err) {
res.redirect('/');
}
res.render('index.ejs', {
title: message,
points: result
});
});
What am I doing wrong? Or better said, how can I pass results of those two queries into the render function?
The cleanest approach here is to use Promises & Promise.all. For that you will need to wrap db.query in a Promise.
// You can use Util.promisify too
const queryWrapper = (statement) => {
return new Promise((resolve, reject) => {
db.query(statement, (err, result) => {
if(err)
return reject(err);
resolve(result);
});
});
};
app.get('/some-route', (req, res, next) => {
const message = "some random text";
Promise.all([
queryWrapper(query),
queryWrapper(query2)
])
.then(([points, types]) => {
res.render('index.ejs', {
title: message,
points,
types
});
})
.catch(err => {
console.error(err);
res.redirect('/');
})
});
Note: If you're using MySQL, the mysql2 package offers promise support, so you can avoid the Promise wrapper.
I found a workaround like this, but I really don't think this is the only, corrent and elegant way how to do it:
let res_points, res_types;
db.query(query, (err, result) => {
if (err) {
res.redirect('/');
}
db.query(query2, (err2, result2) => {
if(err2) {
res.redirect('/');
}
res.render('index.ejs', {
title: "Welcome to Hamburg Map | View Points"
,points: result
,types:result2
});
});
});
Im still learning js, but im trying to write a basic server in nodejs.
The server is supposed to take a query and then return a JSON from a db.
Im trying to use promises but can get my head around this.
My server returns an empty reply.
So what am i doing wrong?
My server file so far (simplified):
http.createServer(function (request, response) {
if (request.method === "GET") {
...
var dbRes = dbFunc.getFromDB(query);
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json');
response.end(JSON.stringify(dbRes));
}
}).listen(port);
My db file so far (simplified):
exports.readHandeler = function (query) {
var promise = new Promise(function(resolve, reject) {
db.collection('data').find(query).toArray(function(err, res) {
if (err) {
throw err;
reject(-1);
}
resolve(res);
}
});
promise.then(function(result) {
return result;
}, function(err) {
return -1;
});
}
My server returns an empty reply. So what am i doing wrong?
Your dbRes will be undefined since you're expecting a response from async promise execution. You need to send response only after promise resolves.
Making this change to your code
http.createServer(function (request, response) {
if (request.method === "GET") {
...
dbFunc.getFromDB(query).then((dbRes) => {
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json');
response.end(JSON.stringify(dbRes));
}).catch((err) => {
console.log('err', err.stack);
});
}
}).listen(port);
Also, you need to return Promise in db execution part.
exports.readHandeler = function (query) {
return new Promise(function (resolve, reject) {
db.collection('data').find(query).toArray(function (err, result) {
if (err) {
return reject(err); // or -1 based on your need
}
return resolve(result);
});
});
};
I am trying to do a nested query with MySql, put the result inside a variable and send over http, but the program always run console.log("test 2:"+rpsData); before the query finish. I already tried this, but still getting the same problem.
const express = require('express')
const app = express()
const mysql = require('mysql');
const Connection = require('mysql/lib/Connection');
const Promise = require('bluebird');
Promise.promisifyAll([
Connection
]);
const connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root123',
database : 'mygallery'
});
app.get('/posts', function(request, response) {
var rpsData;
connection.connectAsync()
.then(function() {
connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
if (err) throw err;
rpsData = rows;
for (var i in rows) {
connection.query('SELECT * FROM image WHERE postCode = ?', [rpsData[i].postCode], function(err, rows, fields) {
if (err) throw err;
rpsData[i].image = rows;
console.log("test 1:"+rpsData);
});
}
});
})
.then(function() {
response.send(rpsData);
console.log("test 2:"+rpsData);
})
.catch(function(error) {
console.error('Connection error.', error);
});
});
What's happening here is you're not tying all of the pieces of async code to the promise chain. Once we convert it to do so this should work.
First lets wrap calls to connection.query to return a promise. We then have to return that generated promise to attach it to the outer promises chain.
If you don't return a promise, it won't know that it has to wait for your code to finish executing and will move forward with the next .then() statement on the outside promise (from connection.connectAsync);
You need to apply the same treatment to the inner query.
Sample code:
app.get('/posts', function(request, response) {
connection.connectAsync()
.then(function() {
return new Promise(function(resolve, reject) {
connection.query('SELECT * FROM post WHERE approved = 1', function(err, rows, fields) {
if (err) reject(err);
resolve(rows.reduce(function(accumulator, current) {
return accumulator.then(function(rpsData){
return new Promise(function(resolve, reject){
connection.query('SELECT * FROM image WHERE postCode = ?', [current.postCode], function(err, rows, fields) {
if (err) reject(err);
current.image = rows;
console.log("test 1:"+rpsData);
resolve(rpsData);
});
});
});
}, Promise.resolve(rows)));
});
});
})
.then(function(rpsData) {
response.send(rpsData);
console.log("test 2:"+rpsData);
})
.catch(function(error) {
console.error('Connection error.', error);
});
});
I'm queueing the internal promises using the technique I describe here