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

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
};
}
})

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
}

Why doesn't my async function return any result?

I wrote this small program to fetch data. This however is done async. Since I nonetheless need to use the function holeVertreter(kzl) as a function in another module, I'd like to get a return value which I can eventually pass on.
Excuse my spaghetti code (I usually prettify the code when I am done with my task ...).
Credentials are stored in a file and are therefore not found in this file.
I'd like to end up with "vertreter" as a return value.
Thank you in advance.
const node = require("deasync");
const DSB = require('dsbapi');
const tabletojson = require('tabletojson');
const https = require('https');
const cred = require("./vertrCred");
const dsb = new DSB(cred["dsb"]["user"], cred["dsb"]["passw"]); //Sanitized - no Credentials here
//Stackoverflow 2332811
String.prototype.capitalize = function(lower) {
return (lower ? this.toLowerCase() : this).replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
};
function holePlan(kuerzel) {
dsb.fetch()
.then(data => {
const timetables = DSB.findMethodInData('timetable', data);
const tiles = DSB.findMethodInData('tiles', data);
var tilesStr = JSON.stringify(tiles["data"][0]["url"]);
var url = JSON.parse(tilesStr);
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end',() => {
var tableasjson = tabletojson.convert(data);
var erstetab = tableasjson[0];
var zweitetab = tableasjson[1];
var drittetab = tableasjson[2];
var viertetab = tableasjson[3];
var fuenftetab = tableasjson[4];
var sechstetab = tableasjson[5];
var siebtetab = tableasjson[6];
var achtetab = tableasjson[7];
if (typeof kuerzel === "undefined")
{
var regenechse = '(Aaa|Aaa[A-Za-z?]|[A-Za-z?]Aaa)';
}
else {
var name = kuerzel.capitalize(true);
var regenechse = '('+name+'|'+name+'[A-Za-z?]|[A-Za-z?]'+name+')';
}
const regex = new RegExp(regenechse,'g');
var sammel = Object.assign(drittetab,fuenftetab);
var z= 0;
var vertreter = {}
var y = JSON.parse(JSON.stringify(sammel));
for (i=0;i<y.length;i++) {
if (typeof y[i].Vertreter =='undefined') {
}
else {
if(y[i].Vertreter.match(regex))
{
z += 1;
vertreter[z] = y[i];
}
}
}
if (z == 0) {
// console.log("Es gibt nichts zu vertreten");
}
else {
//console.log("Es werden "+z+" Stunden vertreten");
return (vertreter);
} ;
});
})
})
.catch(e => {
// An error occurred :(
console.log(e);
});
}
//Stackoverflow
function warte(promise) {
var done = 0;
var result = null;
promise.then(
function (value) {
done = 1;
result = value;
return (value);
},
function (reason) {
done = 1;
throw reason;
}
);
while (!done)
node.runLoopOnce();
return (result);
}
function holeVertretung(kzl) {
var aufgabe = new Promise((resolve,reject) => {
setTimeout(resolve,1000,holePlan(kzl));
});
var ergebnis = warte(aufgabe);
if (typeof ergebnis === "undefined") {
console.log("Mist");
}
else {
console.log(ergebnis);
}
return ergebnis;
}
holeVertretung("Aaa");
That's not the right way to work with promises. If you do such infinite loop, it beats the whole purpose of using promises. Instead, return value from the promise, and use async-await like this:
function warte(promise) {
var done = 0;
var result = null;
return promise.then(
...
}
async function holeVertretung(kzl) {
var aufgabe = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, holePlan(kzl));
});
var ergebnis = await warte(aufgabe);
...
If async-await does not work for some reason, use then clause:
warte(aufgabe).then(value => {
var ergebnis = value;
});

Nodejs request async problem for loop not work

I'm a beginner of nodejs, async bothers me.
I want my code run sequencely or it will breaks.
I have a for loop, and it simply doesn't work...
Here are all the codes:
const util = require('util');
const request = require('request');
const cheerio = require('cheerio');
var host = "http://www.nicotv.me";
var url = "http://www.nicotv.me/video/play/57838-1-%s.html";
var len = 99;
var tab = /-(\d)-/.exec(url);
tab = tab[1] // '1' not '-1-'
function getLen(url) {
//you can ignore this function, it gives len=2
request(url, function (err, response, html) {
if (err) {
console.log('url:', url);
console.log('error:', err);
console.log('statusCode:', response && response.statusCode);
}
else{
var $ = cheerio.load(html);
var cls = '.ff-playurl-dropdown-%s';
$(util.format(cls, tab)).filter(function (){
var data = $(this);
len = data.html().match(/<a href=/g).length;
console.log("episode:", len);
});
getLink(len, function(){
});
}
});
}
getLen(util.format(url, 1)); //len = 2
var getLink = function(lengths, callback){
for (let i = 1; i <= lengths; i++) {
var tmp = util.format(url, i);
try {
request(tmp, function (err, res, html){
console.log('url:', tmp);
if(err){
console.log("error:", err);
console.log("statusCode:", res && res.statusCode);
}else{
var reg = /src="(\/player.php?.{1,})"/;
var result = reg.exec(html);
console.log(result[1]);
}
});
callback();
} catch (error) {
console.log(error);
break;
}
}
}
here is my output:
episode: 2
url: http://www.nicotv.me/video/play/57838-1-2.html
/player.php?u=aHR0cDovL3R5angyLmtpbmdzbnVnLmNuLzM2MHl1bi0xNS5waHA/dmlkPTE1NzkxMzU2MzEyNDAwNTQ5&p=360biaofan&c=0&j=aHR0cDovL2ppZXhpLmtpbmdzbnVnLmNuLzM2MGJpYW9mYW4ucGhwP3VybD0=&x=10&y=&z=
url: http://www.nicotv.me/video/play/57838-1-2.html
/player.php?u=aHR0cDovL3R5angyLmtpbmdzbnVnLmNuLzM2MHl1bi0xNS5waHA/dmlkPTE1Nzg1MDQyMDYyNDAwNTgx&p=360biaofan&c=0&j=aHR0cDovL2ppZXhpLmtpbmdzbnVnLmNuLzM2MGJpYW9mYW4ucGhwP3VybD0=&x=10&y=&z=aHR0cDovL3R5angyLmtpbmdzbnVnLmNuLzM2MHl1bi0xNS5waHA/dmlkPTE1NzkxMzU2MzEyNDAwNTQ5
First problem is these two /player*** link are from 57838-1-1.html
And one of them are not complete.
Second problem is the url output shows 57838-1-2.html twice.
Thanks for your kindly help.
Yesterday had the same problem, so I solved with:
Using request-promise
Replace the loop method arrTitles.Each with for (const jt of arrTitles)
Here a sample:
const request = require('request-promise');
const cheerio = require('cheerio');
var getUrlData =
async function (url) {
console.log(url);
try {
return await request.get(url);
}
catch (err) {
console.error(`${err}: ${url}`);
}
return;
};
var run =
async function (pageUrl) {
var arrData =
await fn.getUrlData(pageUrl)
.then(response => readTable(response));
console.log(arrData);
};
var readTable =
function (document) {
var $;
let arrData = [];
try {
$ = cheerio.load(document);
$('table tr')
.each(
function (trN) {
$(this)
.children('td')
.each(
function (tdN) {
arrData.push($(this).text().trim());
}
)
});
}
catch { }
return arrData;
};
run();

nodejs how to use promise output in other file and modify it and then return its value

I have code in one of 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
Referrence link: Node.js : Call function using value from callback or async
Following is readObjectRepository definition :
readObjectRepository = function(fileName) {
var filePath = '../'+fileName;
xmlObjectRepositoryLoader(filePath)
}
I got the issue in your code. The method readObjectRepository doesn't return a promise and infact doesn't return anything
So you cannot chain .then
To do that- in the function definition of readObjectRepository return the promise of xmlObjectRepositoryLoader
Make this change and it should be all good
readObjectRepository = function(fileName) {
var filePath = '../'+fileName;
return xmlObjectRepositoryLoader(filePath) //This will return the promise of xmlObjectRepositoryLoader which you can handle in 'then' and obtain the text
}

How to transition from async.parallel & series to composable Promises?

I am trying to transition from async to promises and this is what I have. If the code looks contrived it's because I simplified it from what I'm working on to make it easier to grasp. I'm struggling to get the Promise.all to execute.
I commented out the async code that I want to implement in promises:
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs-extra'));
var path = require('path');
var tar = require('tar-fs');
module.exports = Archive;
function Archive() {
var self = this;
var self.base_dir = '/bar/baz',
var self.file1 = 'foo/file1',
var self.file2 = 'foo/file2',
var self.file3 = 'foo/file3',
var self.file4 = 'foo/file4'
}
Archive.prototype.make = function(done) {
var self = this;
// async.series([
// function(next) {
// self._prepareFilesDir(next);
// },
// function(next) {
// self._copyFiles(next);
// },
// function(next) {
// self._writeArchive(next);
// }
// ], done)
self._prepareFilesDir().bind(self)
.then(self._copyFiles.bind(self))
.then(self._writeArchive.bind(self))
.catch(function(e) {
return done(e);
});
};
// ********************************
// * Private functions
// ********************************
Archive.prototype._prepareFilesDir = function() {
var self = this;
return fs.emptyDirAsync(self.base_dir);
};
Archive.prototype._copyFiles = function() {
var self = this;
var sources = {
file1: path.resolve('baz', 'file1'),
file2: path.resolve('baz', 'file2')
file3: path.resolve('baz', 'file3')
file4: path.resolve('baz', 'file4')
file5: path.resolve('baz', 'file5')
};
var destinations = {
file1: path.resolve(self.base_dir, self.file1),
file2: path.resolve(self.base_dir, self.file2),
file3: path.resolve(self.base_dir, self.file3),
file4: path.resolve(self.base_dir, self.file4),
file5: path.resolve(self.base_dir, self.file5)
};
var filters = {
qux: /^qux/,
bru: /^bru/,
blerg: /blerg$/
};
function copyFile1() {
console.log('hello world');
return fs.copyAsync(sources.file2, destinations.file1, { filter: filters.qux });
};
function copyFile2() {
return fs.copyAsync(sources.file2, destinations.file2);
};
function copyFile3() {
return fs.copyAsync(sources.file3, destinations.file3, { filter: filters.bru });
};
function copyFile4() {
return fs.copyAsync(sources.file4, destinations.file4, { filter: filters.blerg });
};
return Promise.all([
copyFile1,
copyFile2,
copyFile3,
copyFile4
]);
// async.parallel([
// copyFile1(next),
// copyFile2(next),
// copyFile3(next),
// copyFile4(next)
// ], function(err) {
// if (err) return done(err);
// done(null);
// })
};
Archive.prototype._writeArchive = function() {
var self = this;
var archive_dir_path = path.resolve(self.base_dir, '..');
var tarPromise = function() {
return new Promise(function(resolve, reject) {
tar.pack(self.files_path)
.pipe(fs.createWriteStream(archive_dir_path + '.tar'))
.on('error', reject)
.on('finish', resolve)
});
};
fs.ensureDirAsync(archive_dir_path)
.then(tarPromise);
};
I must be doing something wrong because the 'hello world' is never printed. I think the stream is promisified correctly but I'm not so sure either. I based my conversion on the promise-nuggets.github.io snippets.
How do I have to do the Promise.all? I'd like to keep separate functions as I think it helps understanding the code better.
Thanks,
the mistakes that I found:
in make method, done would be called only in case of error, suggestion remove done callback, just return promise
again in make, you are doing _prepareFilesDir().bind(self), for staters bind at that point is redundant, it should have been call/apply at that point.
in _writeArchive, you need to return promise, else it ll return undefined and assume that the async function is finished.
updated code in fiddle
There were several issues which #mido22 fixed. There is one more issue though, in Pormise.all(), the functions shouldn't be passed as references but rather executed.
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs-extra'));
var path = require('path');
var tar = require('tar-fs');
module.exports = Archive;
function Archive() {
var self = this;
var self.base_dir = '/bar/baz',
var self.file1 = 'foo/file1',
var self.file2 = 'foo/file2',
var self.file3 = 'foo/file3',
var self.file4 = 'foo/file4'
}
Archive.prototype.make = function() { // CHANGED
var self = this;
// async.series([
// function(next) {
// self._prepareFilesDir(next);
// },
// function(next) {
// self._copyFiles(next);
// },
// function(next) {
// self._writeArchive(next);
// }
// ], done)
return self._prepareFilesDir() // CHANGED
.then(self._copyFiles.bind(self))
.then(self._writeArchive.bind(self)); // CHANGED
};
// ********************************
// * Private functions
// ********************************
Archive.prototype._prepareFilesDir = function() {
var self = this;
return fs.emptyDirAsync(self.base_dir);
};
Archive.prototype._copyFiles = function() {
var self = this;
var sources = {
file1: path.resolve('baz', 'file1'),
file2: path.resolve('baz', 'file2')
file3: path.resolve('baz', 'file3')
file4: path.resolve('baz', 'file4')
file5: path.resolve('baz', 'file5')
};
var destinations = {
file1: path.resolve(self.base_dir, self.file1),
file2: path.resolve(self.base_dir, self.file2),
file3: path.resolve(self.base_dir, self.file3),
file4: path.resolve(self.base_dir, self.file4),
file5: path.resolve(self.base_dir, self.file5)
};
var filters = {
qux: /^qux/,
bru: /^bru/,
blerg: /blerg$/
};
function copyFile1() {
console.log('hello world');
return fs.copyAsync(sources.file2, destinations.file1, { filter: filters.qux });
};
function copyFile2() {
return fs.copyAsync(sources.file2, destinations.file2);
};
function copyFile3() {
return fs.copyAsync(sources.file3, destinations.file3, { filter: filters.bru });
};
function copyFile4() {
return fs.copyAsync(sources.file4, destinations.file4, { filter: filters.blerg });
};
return Promise.all([
copyFile1(), // execute functions
copyFile2(), // idem
copyFile3(), // idem
copyFile4() // idem
]);
// async.parallel([
// copyFile1(next),
// copyFile2(next),
// copyFile3(next),
// copyFile4(next)
// ], function(err) {
// if (err) return done(err);
// done(null);
// })
};
Archive.prototype._writeArchive = function() {
var self = this;
var archive_dir_path = path.resolve(self.base_dir, '..');
var tarPromise = function() {
return new Promise(function(resolve, reject) {
tar.pack(self.files_path)
.pipe(fs.createWriteStream(archive_dir_path + '.tar'))
.on('error', reject)
.on('finish', resolve)
});
};
return fs.ensureDirAsync(archive_dir_path)
.then(tarPromise); // CHANGED,
};

Resources