I'm pretty new to nodejs, I use just ES6 and I massively use generators.
I'm trying to promisfy the "connect" and "query" function of pg module.
Why the following snippet doesn't release the main execution context and keeps the script running from shell? It works correctly but it hangs after printing the result.
Disclaimer: I'm not interested in Bluebird.promisfy function nor pg-promise, knex and bookshelf modules. None of these fits my needs.
'use strict';
let co = require('co');
let pg = require('pg').native;
function connect(hostname, username, password, database) {
let connectionString = `postgres://${username}:${password}#${hostname}/${database}`;
return new Promise((resolve, reject) => {
pg.connect(connectionString, function(error, result, done) {
if(error)
return reject(error);
else
return resolve({ connection: result, release: done });
});
});
}
function query(connection, text, values) {
return new Promise((resolve, reject) => {
connection.query(text, values, (error, result) => {
if(error)
return reject(error);
else
return resolve(result);
});
});
}
co(function*(){
try {
let hostname = 'localhost';
let username = 'damiano';
let password = 'damiano';
let database = 'graph';
let client = yield connect('localhost', 'user', 'pass', 'graph');
let result = yield query(client.connection, `select * from vertices v1, vertices v2 limit 1`);
client.release();
return result;
}
catch(error) {
console.log(error);
}
}).then((result) => {
console.log(result.rows);
return;
});
Edit:
Ok the connection was hanging. The done method doesn't seem to do nothing, there is a hidden method .end on the connection itself.
Even since it is not answer for exact question, I'm still gonna recommend something different. I successfully use for this sequilizejs with koa and co. Sequilize's methods return promises, so I yielding them directly let rows = yield table.findAll({ where: { age: 18 }, raw: true }); And I dont have to worry about connections.
Related
I know this has been asked a lot but I can't seem to use the existing answers to get my code to work. I am trying to use mongojs to make a single query, then put the results in a global (relative to the scope) variable to avoid nesting multiple callbacks. However, I can't seem to get the code to wait for the query to end before continuing.
async function taskmaster() {
const db = mongojs(mongoUri.tasker);
let currentDoc;
const getTask = async function() {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
console.log(doc);
currentDoc = doc;
});
}
await getTask();
console.log(currentDoc);
// Code to process currentDoc below
}
No matter what I do, console.log(doc) shows a valid MongoDB document, yet console.log(currentDoc) shows "undefined". Thanks in advance!
Inside your async function, you use findOne method() in callback style, so it's totally normal that console.log(currentDoc) shows undefined, because it executes before
currentDoc = doc;
You can promisify the findOne method, to use it with async/await keyword.
I found a tutorial to promisfy a callback style function here, hope it help : https://flaviocopes.com/node-promisify/
--- EDIT ---
I rewrite your code when promising the findOne method, as suggested by O.Jones
async function taskmaster() {
const getTask = async (taskId) => {
return new Promise((resolve, reject) => {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
if(err) {
console.log("problem when retrieve data");
reject(err);
} else {
resolve(doc);
}
});
})
const db = mongojs(mongoUri.tasker);
const currentDoc = await getTask(taskId);
console.log(currentDoc);
}
This code returns undefined when it should be data from the Database. It is part of a class which has the variable this.database (an sqlite3 database). What have I done wrong?
getData(chip_id) {
let user_name;
this.database.get("SELECT data FROM chips WHERE id=?",[chip_id],(err,row) => {
user_name = row.data;
});
return user_name;
}
Retrieving data from database is an asynchronous action and you should handle it with something like Promise or async function. Change your function to this.
getData(chip_id) {
return new Promise((resolve, reject) => {
let user_name;
this.database.get("SELECT data FROM chips WHERE id=?", [chip_id], (err, row) => {
if(err) {
reject(err);
}
else {
user_name = row.data;
resolve(user_name);
}
});
});
}
and in the client side:
getData(id)
.then(username => {
// the username is ready to use
})
.catch(error => {
// handle the error
});
Reading these links also helps you a lot to start:
Callbacks Vs Promises and basics of JS
Getting to know asynchronous JavaScript: Callbacks, Promises and Async/Await
Converting callbacks to promises
I'm currently trying to send 2 objects to the front .hbs front end. However I cant seem to work out how to do this because I'm using promises.
Currently, my thinking is i perform the sql query, the country and organisation name is extracted, and then each sent to a geocoding api, returned and then squashed together in the same promises. But i'm not sure how to extract these for the render function.
Node
//route for homepage
app.get('/', (req, res) => {
let sql = "SELECT org_name, country_name from places;
let query = conn.query(sql, (err, results) => {
if (err) throw err;
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Promise.all(promises).then((geoLoc, geoBus) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
});
});
Front end call
results1 = {{{results}}}
console.log(results1.length)
business1 = {{{businesses}}}
console.log(business1.length)
Wrap your geo.geocode into a Promise
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
Combine both calls to geo.geocode
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Call them
Promise.all(promises).then(([geoLoc, geoBus]) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
As MadWard's answer mentions, deconstructing the argument of the callback of Promise.all is necessary since everything will be in the first argument. Make sure you check out his post for more details
Something important to recall: you will never have more than one argument in a then() callback.
Now you may ask: in the case of Promise.all(), what is this value?
Well, it is an array with all the values from the promises it awaits, in the order in which they are called.
If you do:
Promise.all([
resolveVariable1, resolveVariable2, resolveVariable3
]).then((values) => {
})
values will be [variable1, variable2, variable3], the three variables that the promises resolve to.
Your case is, however, a bit more complicated. What is gonna be returned at the end is a 2-D array containing every entry. It is an array of length results.length, and each of its element has a length of 2. The first element is the result, and the second one is the business.
Here is your snippet:
Promise.all(promises)
.then((values) => {
let results = values.map(elmt => elmt[0]);
let businesses = values.map(elmt => elmt[1]);
res.render('layouts/layout', {
results: JSON.stringify(results),
businesses: JSON.stringify(businesses)
});
})
I'm new to Express framework and learning, I'm having a problem using .then. The problem is I have 2 functions and I want the first one to complete before the second to start executing. I'm exporting the modules.
var ubm = require('./userBasic');
There are 2 functions setUserData and showUserId, the showUserId must execute only after setUserData has performed its operation.
var userId = ubm.setUserData(userName,userEmail,userDOB,moment);
userId.then(ubm.showUserId(userId));
Below are the 2 functions:
module.exports = {
setUserData: function (userName,userEmail,userDOB,moment){
//Performing database activities
return userId;
}
showUserId: function (userId){
console.log(userId);
}
}
When i run it says TypeError: Cannot read property 'then' of undefined.
Like I said I'm very new and learning and was unable to figure out the solution. I did some google search and got a brief about promise, but I don't know how to implement here.
Try using promises
module.exports = {
setUserData: function(userName, userEmail, userDOB, moment) {
return new Promise(function(resolve, reject) {
//db stuff
reject(error);
resolve(userId);
});
},
showUserId: function(userId) {
console.log(userId);
};
};
So in your execution you would write
ubm.setUserData(username, usereEmail, userDOB, moment)
.then((data) => {
showUserId(data);
})
.catch((err) => {
console.log(err);
});
A couple of things to note is that in this instance you could just log data without the need for another function like
ubm.setUserData(username, usereEmail, userDOB, moment)
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
Whatever value you pass into resolve() will be returned as well as you pass errors into reject().
i am trying to write a unit test for my node.js code . I am able to write the test for a function but i want to test each network query(query fetching data from DB) whether they are returning desired response or not.
I am sharing my node.js code as well as my test code.
node.js code(routes.js)
module.exports = {
getUser: {
get: function(req, parameters, environment) {
var id = req.queryString.id;
return new Promise(function(resolve, reject) {
db.tx(t =>{
return t.one("select configuration_value from fint.configuration where configuration_key='LOAN' and application_id =(select application_id from fint.application where application_name='Product Tailoring')")
})
.then(conf => {
conf_key = conf.configuration_value;
})
db.tx(t =>{
return t.one("select smart_account_type_id from fint.smart_account_type where smart_account_type_name='LOAN'")
})
.then(acc_type => {
smart_acc_type_id = acc_type.smart_account_type_id;
})
}
})
}
}
This is a sample code, what exactly i want is to run the test on the individual queries instead of the whole function.
My test code (test.js)
var expect = require('chai').expect;
var Promise = require('bluebird');
var chai = require('chai');
var app = require('../api/smartAccount/identifyLoan/getDirectDebitTrans/routes');
describe("Unit testing for function", function(){
it("Testing the function using mocha", function(done){
var req = {
queryString: {
uniqueUserId: 101
}
};
var test = app.getUser.get(req);
return expect(Promise.resolve(test)).to.have.property('_bitField');
done();
});
});
Any leads will be appreciated, and if anybody come across some links regarding the same, please share.
TIA
First of all, if you want to test each query separately, I would refactor the code and wrap each query in a separate function. Thus, you will be able to do unit testing over real units. If you're not able to test a part of your app easily, it's probably because something isn't designed the right way. By the way, you return a promise that never resolves (or rejects)!?
About the unit testing with promise function, you could give a try to co-mocha. Using such a flow control framework will make your promise tests much more readable.
module.exports = {
getUser: {
get: function(req, parameters, environment) {
var id = req.queryString.id;
return new Promise(function(resolve, reject) {
getConfKey(id)
.then(confKey => {
getAccType(id)
.then(accType => {
resolve({key: confKey, type: accType})
})
})
})
})
getConfKey: function(id) {
return new Promise(function(resolve, reject) {
db.tx(t =>{
return t.one("select configuration_value from fint.configuration where configuration_key='LOAN' and application_id =(select application_id from fint.application where application_name='Product Tailoring')")
})
.then(conf => {
resolve(conf.configuration_value);
})
})
}
getAccType: function(id) {
return new Promise(function(resolve, reject) {
db.tx(t =>{
return t.one("select smart_account_type_id from fint.smart_account_type where smart_account_type_name='LOAN'")
})
.then(acc_type => {
resolve(acc_type.smart_account_type_id);
})
})
}
}
}
Of course, this is an example. If both functions are independent, you can execute these concurrently. Concurrency is not the point of this question.