Promise is pending - node.js

I have a class in my nodejs application with the following code:
var mongoose = require('mongoose');
var Roles = mongoose.model('roles');
var Promise = require("bluebird");
module.exports = Role;
var err = null;
var id;
function Role(name, companyId) {
this.err = err;
this.name = name;
this.companyId = companyId;
this.id = getId(name, companyId);
}
var getId = function (name, companyId) {
return new Promise(function(resolve, reject) {
Roles.findOne({companyId:companyId, name:name}, function(err,result) {
resolve(result._id);
});
});
};
When I am calling the class, the id is pending:
var currentRole = new Role(myRole, comId);
console.log(currentRole);
How can I get the values from the class when they are resolved?

currentRole.id is a promise so you can call then() on it to wait for it to be resolved:
var currentRole = new Role(myRole, comId);
currentRole.id.then(function (result) {
// do something with result
});
This feels like a strange API though, you expect your object to be "ready to use" when its constructor returns. Might be better to have getId be a promise returning function on the prototype of Role so you instead do something like:
var currentRole = new Role(myRole, comId);
currentRole.getId().then(function (result) {
// do something with result
});
You should also consider handling that error to reject the promise:
var getId = function (name, companyId) {
return new Promise(function(resolve, reject) {
Roles.findOne({companyId:companyId, name:name}, function(err,result) {
if (err) {
return reject(err);
}
resolve(result._id);
});
});
};
and add a rejection handler to your call to getId:
var currentRole = new Role(myRole, comId);
currentRole.getId().then(function (result) {
// do something with result
}, function (err) {
// do something with err
});
or equivalently:
var currentRole = new Role(myRole, comId);
currentRole.getId().then(function (result) {
// do something with result
}).catch(function (err) {
// do something with err
});

Related

How to call another prototype async function from another in the same class

I have one async prototype function and that is called from another async prototype function. But, I got the error below. How to resolve?
I use node.js v14.1.0.
UnhandledPromiseRejectionWarning: TypeError: this.saySomething2 is not a function
const Person = function() {
console.log("CALLED PERSON");
};
Person.prototype.saySomething = async function() {
this.saySomething2();
};
Person.prototype.saySomething2 = async function() {
//do something
console.log("hello");
};
(async function() {
var ape = new Person();
await ape.saySomething();
}());
update: adding actual code.
// constructor function for the KingInfo class
function KingInfo(
date,
employeeKey,
workDayTypeName) {
this._date = date;
this._employeeKey = employeeKey;
this._workDayTypeName = workDayTypeName;
}
KingInfo.prototype.sync = async function(){
var isSucceed = false;
let syncType = await this.getSyncType();
if(syncType === "post"){
}
else if(syncType === "patch"){
}
else{
}
return isSucceed;
}
//#return: "post", "patch", "none"
KingInfo.prototype.getSyncType = async function(){
let syncType = "none";
let sql = [];
sql.push("SELECT count(*) as count");
...
let records = await db_module.query(sql.join(""));
let count = records[0].count;
if(count <= 0){
syncType = "post";
}
else{
let isSame = await this.compareToDataInDB();
if(!isSame){
syncType = "patch";
}
}
return syncType;
}
KingInfo.prototype.compareToDataInDB = async function(){
let date = this._date;
let empKey = this._employeeKey;
let sql = [];
sql.push("SELECT workDayTypeName");
...
let records = await db_module.query(sql.join(""));
let record = records[0];
let res = false;
if(workDayTypeName === record.workDayTypeName){
res = true;
}
return res;
}
(async function(){
let date = "2020-08-01";
let employeeKey = "";
let workDayTypeName = "aaaaa";
let kingInfo = new KingInfo(
date,
employeeKey,
workDayTypeName);
kingInfo.sync();
}());
module.exports = {
KingInfo: KingInfo
}
I got this error:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property '_date' of undefined
UnhandledPromiseRejectionWarning: TypeError: this.compareToDataInDB is not a function
update:
adding code for db module.
this module might effect badlly?
db.js
"use strict";
const mysql = require('mysql');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'something'
});
function query(sql){
return new Promise(function(resolve, reject) {
connection.connect((err) => {
if (err) throw err;
connection.query(sql, function (err, result, fields) {
if (err) throw err;
resolve(result);
});
});
});
}
module.exports = {
query: query
}

Can't return data from callback function at NodeJS

When i create new variable and assign callback function, But data cannot return from callback function. Undefined is occurring at new variable.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
console.log(s);
var s is undefined! ....
You are mixing up callback and Promise which are two different way to handle asynchronous calls.
I recommend you to use of Promises because they are simpler and the present and future of javascript.
Using async/await which is the next step after Promises
const user = {
find: () => ['jon', 'kalessi', 'jorah'],
};
async function getUsers() {
return (await user.find({})) || [];
}
async function myJob() {
const users = await getUsers();
console.log(users);
// Put your next code here
}
myJob();
Full promise :
const user = {
find: () => new Promise((resolve) => resolve(['jon', 'kalessi', 'jorah'])),
};
user.find({})
.then((docs = []) => {
console.log(docs);
// Put you next code here, you can use of the variable docs
})
.catch((err) => {
console.log(err);
});
Full callback :
const user = {
find: (_, callback) => callback(false, ['jon', 'kalessi', 'jorah']),
};
user.find({}, (err, docs = []) => {
if (err) {
console.log(err);
} else {
console.log(docs);
// Put you next code here, you can use of the variable docs
}
});
I think user.find returning the promise. you can do like this.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
Promise.all(s)
.then(result => {
console.log(result);
});
Otherwise you can also use await Like this:
async function abc(){
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = await user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
}
because await worked with async thats why i put it into async function.

how can access property of class in promise?

I have a class that have one method .I want change return type of my method to promise . but in promise can not access to properties of class .how can resolve this .But get this exception
Reason: TypeError: Cannot read property 'bot' of undefined
const SDK = require('balebot');
const Promise = require('bluebird');
import incoming from './incoming';
const _ = require('lodash');
class Bale {
constructor(bp, config) {
if (!bp || !config) {
throw new Error('You need to specify botpress and config');
}
this.bot = null;
this.connected = false;
this.bot = new SDK.BaleBot(config.botToken);
bp.logger.info('bale bot created');
}
setConfig(config) {
this.config = Object.assign({}, this.config, config);
}
sendText(chat, text, options) {
let msg = new SDK.TextMessage(text);
return new Promise(function (resolve, reject) {
var response = this.bot.send(msg, receiver);
if (response) {
reject(err);
} else {
resolve(response);
}
});
}
}
module.exports = Bale;
You need to bind this or use Arrow functions to preserve this context:
const SDK = require('balebot');
const Promise = require('bluebird');
import incoming from './incoming';
const _ = require('lodash');
class Bale {
constructor(bp, config) {
if (!bp || !config) {
throw new Error('You need to specify botpress and config');
}
this.bot = null;
this.connected = false;
this.bot = new SDK.BaleBot(config.botToken);
bp.logger.info('bale bot created');
}
setConfig(config) {
this.config = Object.assign({}, this.config, config);
}
sendText(chat, text, options) {
let msg = new SDK.TextMessage(text);
// Use an arrow function instead of function
return new Promise((resolve, reject) => {
var response = this.bot.send(msg, receiver);
if (response) {
reject(err);
} else {
resolve(response);
}
});
}
}
module.exports = Bale;
This would work
sendText() {
return new Promise((resolve, reject) => {
console.log(this.bot); // it will not be undefined
});
}
The reason this works is because arrow functions lexically bind their context so this actually refers to the originating context.

Completing loops inside of node.js promises

I am trying to complete a few loops over firebase objects using .forEach and I am also using promises. This isn't working out how I had planned it. My basic problem is that the loops inside of my promises complete well after the promise chain itself completes. Here is my function:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "";
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
console.log('one');
progress(20);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
console.log('two');
progress(40);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
console.log('three');
progress(60);
});
}).then(function() {
posts.once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
var allPosts = postSnapshot.val()
var postKeys = Object.keys(allPosts)
return Promise.all(postKeys.map(function(postKey) {
var postID = postKey;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
return itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
console.log('post snapshot'+ itemData);
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
}));
});
}).then(function() {
// Move to next item
return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
}).then(function() {
// Move to next item
return console.log('second one'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
And here is the output:
one
two
three
hey look hereundefined
second oneundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
I think I might be using promises incorrectly but I don't know.
I figured out that I should be using Promise.all() in order to wait for my forEach loops to complete. Here is the code I used to solve my problem, enjoy:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "-KZOO0UII67uOmYo6DJh";
var postKeys = [];
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
progress(10);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
progress(40);
});
}).then(function() {
progress(55);
return posts.orderByChild('receipt').equalTo(receiptID).once('value');
}).then(function(postSnapshot) {
return postSnapshot.forEach(function(post) {
progress(70);
postKeys.push(post.key)
});
}).then(function() {
return Promise.all(postKeys.map(function(postKey) {
return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
});
})).then(function(results) {
progress(85);
return results;
});
}).then(function() {
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
resolve();
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});

Node.js : Call function using value from callback or async

I have written below .js file to call below defined function.
objectRepositoryLoader.readObjectRepository() returns me a hashmap from where i have to use values in enterUserName(), enterPassword(), clickLoginButton() functions.
var path = require('path');
var elementRepoMap = {}
var LandingPage = function(){
var fileName = path.basename(module.filename, path.extname(module.filename))
objectRepositoryLoader.readObjectRepository(fileName+'.xml' , function(elementRepo){
console.log(elementRepo) //values are being printed here
this.elementRepoMap = elementRepo
});
this.enterUserName = function(value){
console.log(elementRepoMap) //values are not being printed here
//Some Code
};
this.enterPassword = function(value){
//Some Code
};
this.clickLoginButton = function(){
//Some Code
};
};
module.exports = new LandingPage();
The objectRepositoryLoader.readObjectRepository() function defined in another file is as below:
var ObjectRepositoryLoader = function() {
this.readObjectRepository = function(fileName, callback) {
var filePath = './elementRepository/'+fileName;
this.loadedMap = this.objectRepoLoader(filePath, function(loadedMap){
return callback(loadedMap);
});
}
this.objectRepoLoader = function(filePath, callback){
if (filePath.includes(".xml")) {
this.xmlObjectRepositoryLoader(filePath, function(loadedMap){
return callback(loadedMap);
});
}
this.xmlObjectRepositoryLoader = function (xmlPath, callback){
var innerMap = {};
var elementName;
fs.readFile(xmlPath, "utf-8",function(err, data) {
if(err){
console.log('File not found!!')
}
else{
var doc = domparser.parseFromString(data,"text/xml");
var elements = doc.getElementsByTagName("A1");
for(var i =0 ; i< elements.length;i++){
var elm = elements[i];
elementName = elm.getAttribute("name");
var params = elm.getElementsByTagName("AS");
innerMap = {};
for(var j =0 ; j< params.length;j++){
var param = params[j];
var locatorType = param.getAttribute("type");
var locatorValue = param.getAttribute("value");
innerMap[locatorType] = locatorValue;
}
loadedMap[elementName] = innerMap;
innerMap={};
};
}
return callback(loadedMap);
});
};
How can I call enterUserName(), enterPassword(), clickLoginButton() function from spec.js file and is there any way I can avoid using callback and use async.js and call enterUserName(), enterPassword(), clickLoginButton() from spec.js file ?
EDIT
I have modified my file like below:
this.xmlObjectRepositoryLoader = function (xmlPath){
var innerMap = {};
var elementName;
var filePath = xmlPath+'.xml'
var self = this
return new Promise(
function(resolve, reject){
console.log("In xmlObjectRepositoryLoader : "+filePath)
self.readFilePromisified(filePath)
.then(text => {
var doc = domparser.parseFromString(text,"text/xml");
var elements = doc.getElementsByTagName("Element");
for(var i =0 ; i< elements.length;i++){
var elm = elements[i];
elementName = elm.getAttribute("name");
var params = elm.getElementsByTagName("param");
innerMap = {};
for(var j =0 ; j< params.length;j++){
var param = params[j];
var locatorType = param.getAttribute("type");
var locatorValue = param.getAttribute("value");
innerMap[locatorType] = locatorValue;
}
map[elementName] = innerMap;
innerMap={};
}
console.log(map) // prints the map
resolve(text)
})
.catch(error => {
reject(error)
});
});
}
this.readFilePromisified = function(filename) {
console.log("In readFilePromisified : "+filename)
return new Promise(
function (resolve, reject) {
fs.readFile(filename, { encoding: 'utf8' },
(error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
})
})
}
I am calling above function from another file as below:
objectRepositoryLoader.readObjectRepository(fileName)
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
But it gives me error as
.then(text => { ^
TypeError: Cannot read property 'then' of undefined
In this case how can I use promise to call another promise function and then use the returned value in one more promise function and return calculated value to calling function where I can use the value in other functions. I sound a bit confused. Please help
You can use async.waterfall and async.parallel to perform this task
see the reference
I just tried your code to make it working, I explained the way of implementation in comment.
async.waterfall([
function(next){
objectRepositoryLoader.readObjectRepository(fileName+'.xml' ,next)//pass this next as parameter in this function defination and after manipulation return result with callback like this(null,result)
}
],function(err,result){
if(!err){
//Do wahtever you want with result
async.parallel([
function(callback){
this.enterUserName = function(value){
console.log(elementRepoMap)
//Some Code
};
},
function(callback){
this.enterPassword = function(value){
//Some Code
};
},
function(callback){
this.clickLoginButton = function(){
//Some Code
};
}
], function(err, results) {
// optional callback
};
}
})

Resources