Calling a Promise Method from another Promise - javascript

Hope everyone of you are doing well. I am stuck in a bit of problem. Any help will be highly appreciated.
I am using ldapJS and I want to call another promise from searchEntry method of LDAPJS. When i write the code same as below. it throws an error
exports.getADUsers = async (reqst, resp, next) => {
let flag = false;
var finalList = [];
let adConnStatus = await ConnectAD()
.then().catch((errConnStatus) => {
console.log("Error in connection with Active directory " + errConnStatus);
});
if (adConnStatus.status == 0) {
let client = adConnStatus.client;
const opts = {
filter: '(ObjectClass=*)',
scope: 'sub',
attributes: ['cn', 'sid', "objectSid"]
};
client.search('dc=domain,dc=com', opts, (err, res) => {
if (err) {
console.log("Error: " + err);
} else {
res.on('searchEntry', (entry) => {
finalList = await searchEntry(entry);
});
res.on('searchReference', (referral) => {
console.log('referral: ' + referral.uris.join());
});
res.on('error', (err) => {
console.error('error: ' + err.message);
});
res.on('end', (result) => {
resp.send(finalList);
console.log(result);
});
}
});
}
}
Error:
finalList = await searchEntry(entry);
^^^^^
SyntaxError: await is only valid in async function
Please help! how to call another promise from one promise. Though the method is async why it shows this message? what am i doing wrong?
EDIT
After adding the keyword async as suggested by #Punth. Also modifying a bit of the code. my new code is as follows.
exports.getADUsers = async (reqst, resp, next) => {
let adConnStatus = await ConnectAD()
.then().catch((errConnStatus) => {
console.log("Error in connection with Active directory " + errConnStatus);
});
if (adConnStatus.status == 0) {
var adUsersList = [];
let client = adConnStatus.client;
const opts = {
filter: '(ObjectClass=*)',
scope: 'sub',
attributes: ['cn', 'sid', "objectSid"]
};
client.search('dc=domain,dc=com', opts, (err, res) => {
if (err) {
console.log("Error: " + err);
} else {
res.on('searchEntry', async (entry) => {
var raw = entry.raw;
if (raw.objectSid != "undefined" && raw.objectSid != null && entry.object.cn != null && entry.object.cn != "undefined") {
let userData = {
"Name": entry.object.cn,
"SSID": sidBufferToString(raw.objectSid)
}
var lmn = await ConnectAndGetUsersList(userData.SSID);
userData["XYZ"] = lmn.xyz;
userData["ABC"] = lmn.abc;
adUsersList.push(userData);
}
});
res.on('searchReference', (referral) => {
console.log('referral: ' + referral.uris.join());
});
res.on('error', (err) => {
console.error('error: ' + err.message);
});
res.on('end', (result) => {
console.log(result);
resp.send(adUsersList);
});
}
});
}
}
By running the above code it doesn't shows me anything. res.on('end' ....) is called prior to res.on("searchEntry"....) Therefore the array of adUsersList is null.
Now my question is how to resp.send the final arraylist???

Update this line as follows, to invoke promises using async/await syntax inside a callback you need to make it async
res.on('searchEntry', async (entry) => {
finalList = await searchEntry(entry);
});

Related

How can I return value from this child function in javascript

cannot return the dat[0] value from the inner function
let ite = fs.readdir(directoryPath, function (err, files) {
if (err) {
return console.log('Unable to scan directory: ' + err);
}
dat = files.filter(item => item[1] == files.length);
return dat[0];
});
You are returning from the function which will called when the operation will be finished. So returning from that function doesn't change the value of your variable.
You can use readdirSync
const res = fs.readdirSync(directoryPath);
If you don't want it to be sync you can use fs.promises with async await
let res;
(async function() {
res = await fs.promises.readdir("");
})();
I don't recommend to read sync reading because it can block the main thread. You could use a callback function by passing it as parameter to another function.
function getData(onSuccess, onError) {
fs.readdir(directoryPath, function (err, files) {
if (err) {
onError('Unable to scan directory: ' + err);
}
dat = files.filter(item => item[1] == files.length);
onSuccess(dat[0]);
});
}
getData(function(data){
console.log(data);
}, function(error){
//error here
})
We can make it as a promise method to run the code async. Here we don't block any thread. It will be running asynchronously.
function getData(directoryPath) {
return new Promise((resolve, reject) => {
fs.readdir(directoryPath, function (err, files) {
if (err) {
reject('Unable to scan directory: ' + err.message);
}
dat = files.filter(item => item[1] == files.length);
resolve(dat[0]);
});
});
}
getData(directoryPath).then((data) => {
console.log(data);
}).catch((err) => {
console.error(err);
});
// or
try {
let data = await getData(directoryPath);
} catch (error) {
console.error(error);
}

Async await not working properly in function call with callbacks

I am new to node.js.I am using await/async to avoid function to run before completing previous one.First of all here is code.
---------------first-----------
exports.createProfileUser = async (req, res, next) => {
const {user_image,image_two,
image_three,image_four,
image_five} = req.files
try{
var filetype = req.files.user_image.name.split(".");
var newFileName = "./public/images/"+Date.now()
+ Math.floor(100000 + Math.random() * 900000)
+ "."+filetype[1]
await insertImages(req,newFileName)
await createProfile(req,res,newFileName.substring(1), (result) =>{
if(result){
return res.json(result)
}
else{
return res.json({
status:false,
message:'Error Occurred '
})
}
})
. then((result) => {
if(result){
return res.json(result)
}
else{
return res.json({
status:false,
message:'Error Occurred '
})
}
})
}
catch(error){
res.sendStatus(500) && next(error)
}};
In above code i want insertImages function to be fully finished before calling createProfile.But that's not happening.Here is insertImages code.
--------------two--------------
insertImages = (req,baseFileName) =>{
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
fs.copyFile(req.files.image_five.path,
name,
async(err) => {
if (err) {
console.log("Error in copying file",err)
return res.json({
status:false,
message:"Error occurred while updating Profile Pic Two of the user ",
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
});
}
}
After copying file i am saving a object in gallery schema and after that get its _id and update that to users schema.Code for that is.
--------------threee----------------
makeGallery = async (user_id,imageName,callback) => {
var g = new Gallery();
g.image = imageName
var saved = await g.save()
.then( async (gallery) => {
await Users.findOneAndUpdate({user_id: user_id},{$push: {gallery:[gallery._id]}},
{ upsert: true }
)
.then(async(data) =>{
await console.log("Successfully added");
})
await callback(gallery._id)
})
.catch(err => {
return {
status:false,
message:err.message,
}
});
}
I have used await so function will wait till data updates. But that's not working what i expected.createProfile function is called before insertImages is finished.What i am doing wrong here?Please correct my code i am stuck in this issue.Thanks in advance.
Mainly you are not synchronizing with fs.copyFile which is asynchronous
>>> insertImages = async (req,baseFileName) => {
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
>>> await new Promise(function(resolve, reject){
>>> return fs.copyFile(req.files.image_five.path, name, err => {
if (err) {
console.log("Error in copying file",err)
// you should return the response server elsewhere. NOT in insertImages..
// you should throw a proper error as well
>>> return reject(res.json({
status:false, // should be 500 for internal server error
message:"Error occurred while updating Profile Pic Two of the user ",
}))
}
>>> return resolve()
})
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
}
Eventually, you may use
const copyFile = require('util').promisify(fs.copyFile)
so you can directly write
>>> insertImages = async (req,baseFileName) => {
const {user_id} = req.fields
var newFileNameTemp = baseFileName.substr(1);
if(image){
let fileType = req.files.image_five.name.split(".")
let name = "./public/images/"+Date.now() + "." + fileType[1]
let tempName = name.substr(1);
>>> await copyFile(req.files.image_five.path, name).catch(err => {
console.log("Error in copying file",err)
>>> throw res.json({
status:false,
message:"Error occurred while updating Profile Pic Two of the user ",
})
})
}
await makeGallery(user_id,tempName, (resultId)=>{
console.log('resultId,',resultId)
})
}

Use async await inside mapping function

In my Node Express App, I want to get all comments for a lesson and modify each comment by adding a fullname field to each comment. For getting full name of a user, I have defined findFullNameByUserId function in UserController.js. I use findAllCommentsByLessonId() in CourseController.js as follows. However, when I'm using it, I always get an empty array. How can I use findFullNameByUserId in findAllCommentsByLessonId() so that fullname field can be added to each comment object?
CourseController.js
findAllCommentsByLessonId: async (courseId,lessonId,callback) => {
Course.find({_id: courseId}, function(err, course) {
if (err) {
callback(err, null);
} else {
const lesson = course[0].lessons.filter(l => l._id !== lessonId)
const comments = lesson[0].comments.map( async c => {
try{
const name = await UserController.findFullNameById(c.user)
return (
{
userId: c.user,
name: name,
content: c.content
}
)
}
catch(err){
console.log(err)
}
})
// console.log(comments) --> This always prints [{}]
callback(null, comments)
}
});
}
UserController.js
module.exports = {
findFullNameById: async (userId) => {
await User.find({_id: userId}, function(err, user) {
if (err) {
console.log(err)
} else {
return user[0].name+" "+( user[0].lname ? user[0].lname : "")
}
});
}
}
in CourseController.js either you can use async-await or you can use callback
async-await way :
findAllCommentsByLessonId: async (courseId,lessonId) => {
let course = await Course.findOne({_id: courseId});
if (course){
let lesson = course.lessons.find(l => l._id == lessonId);
let comments = [];
for(let comment of lesson.comments){
let name = await UserController.findFullNameById(comment.user);
comments.push({userId: comment.user,name: name,content: comment.content});
}
return comments;
}else{
return [];
}
}
callback way :
findAllCommentsByLessonId: (courseId,lessonId,callback) => {
Course.findOne({_id: courseId},function(err, course) {
if (err) {
callback(err, null);
} else {
let lesson = course.lessons.find(l => l._id == lessonId);
let comments = lesson.comments;
comments.map((comment)=>{
UserController.findFullNameById(comment.user).then(name=>{
return {userId: comment.user,name: name,content: comment.content};
});
});
callback(null, comments);
}
});
}
Start by dropping callbacks and actually using promises for await. You haven't specified what find is, but chances are it's a modern library supporting promises. So write
async function findAllCommentsByLessonId(courseId, lessonId) {
const [course] = Course.find({_id: courseId};
const lesson = course.lessons.find(l => l._id !== lessonId);
const comments = lesson.comments.map(async c => {
const name = await UserController.findFullNameById(c.user)
return {
userId: c.user,
name,
content: c.content
};
});
}
module.exports.findFullNameById = async (userId) => {
const [user] = await User.find({_id: userId});
return user.name + " " + (user.lname ? user.lname : "");
};
Then you'll notice that comments is not an array of users, but rather an array of promises for users, so wrap it in a await Promise.all(…) call.
As #SuleymanSah commented, I tried to use .populate and it worked well. I think this is the correct approach as for the exact reasons he's pointed out. The following is how I did it:
Lesson.findOne({ _id: lessonId }).
populate('comments.user').
exec(function(err, lesson) {
if (err) {
console.log(err);
return callback(err, null);
}
if (!lesson) {
console.log("No record found");
return callback(err, null);
}
return callback(null, lesson.comments);
});

AWS Lambda function handler not inserting to Athena

I'm using a snippet example for Amazon Athena just to test inserting some data. I can't tell why it isn't working and CloudWatch logs does not show any output when the statement execution is completed. Even when I change it to a simple select statement I can't see any output. I know the query, database and table is fine, because when I test it using the Athena query editor it executes without a problem.
module.exports.dlr = async event => {
let awsFileCreds = {
accessKeyId: "XXX",
secretAccessKey: "XXX"
};
let creds = new AWS.Credentials(awsFileCreds);
AWS.config.credentials = creds;
let client = new AWS.Athena({ region: "eu-west-1" });
let q = Queue((id, cb) => {
startPolling(id)
.then(data => {
return cb(null, data);
})
.catch(err => {
console.log("Failed to poll query: ", err);
return cb(err);
});
}, 5);
const sql = "INSERT INTO delivery_receipts (status, eventid, mcc, mnc, msgcount, msisdn, received, userreference) VALUES ('TestDLR', 345345, 4353, '5345435', 234, '345754', 234, '8833')"
makeQuery(sql)
.then(data => {
console.log("Row Count: ", data.length);
console.log("DATA: ", data);
})
.catch(e => {
console.log("ERROR: ", e);
});
function makeQuery(sql) {
return new Promise((resolve, reject) => {
let params = {
QueryString: sql,
ResultConfiguration: { OutputLocation: ATHENA_OUTPUT_LOCATION },
QueryExecutionContext: { Database: ATHENA_DB }
};
client.startQueryExecution(params, (err, results) => {
if (err) return reject(err);
q.push(results.QueryExecutionId, (err, qid) => {
if (err) return reject(err);
return buildResults(qid)
.then(data => {
return resolve(data);
})
.catch(err => {
return reject(err);
});
});
});
});
}
function buildResults(query_id, max, page) {
let max_num_results = max ? max : RESULT_SIZE;
let page_token = page ? page : undefined;
return new Promise((resolve, reject) => {
let params = {
QueryExecutionId: query_id,
MaxResults: max_num_results,
NextToken: page_token
};
let dataBlob = [];
go(params);
function go(param) {
getResults(param)
.then(res => {
dataBlob = _.concat(dataBlob, res.list);
if (res.next) {
param.NextToken = res.next;
return go(param);
} else return resolve(dataBlob);
})
.catch(err => {
return reject(err);
});
}
function getResults() {
return new Promise((resolve, reject) => {
client.getQueryResults(params, (err, data) => {
if (err) return reject(err);
var list = [];
let header = buildHeader(
data.ResultSet.ResultSetMetadata.ColumnInfo
);
let top_row = _.map(_.head(data.ResultSet.Rows).Data, n => {
return n.VarCharValue;
});
let resultSet =
_.difference(header, top_row).length > 0
? data.ResultSet.Rows
: _.drop(data.ResultSet.Rows);
resultSet.forEach(item => {
list.push(
_.zipObject(
header,
_.map(item.Data, n => {
return n.VarCharValue;
})
)
);
});
return resolve({
next: "NextToken" in data ? data.NextToken : undefined,
list: list
});
});
});
}
});
}
function startPolling(id) {
return new Promise((resolve, reject) => {
function poll(id) {
client.getQueryExecution({ QueryExecutionId: id }, (err, data) => {
if (err) return reject(err);
if (data.QueryExecution.Status.State === "SUCCEEDED")
return resolve(id);
else if (
["FAILED", "CANCELLED"].includes(data.QueryExecution.Status.State)
)
return reject(
new Error(`Query ${data.QueryExecution.Status.State}`)
);
else {
setTimeout(poll, POLL_INTERVAL, id);
}
});
}
poll(id);
});
}
function buildHeader(columns) {
return _.map(columns, i => {
return i.Name;
});
}
return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Figured it out. Using aws lambda events with athena is easy using the athena-express package. You can specify your configuration and query the athena database like you normally would with significantly less code than what's provided in the amazon athena nodejs example.
This is the code I used to achieve a result:
"use strict";
const AthenaExpress = require("athena-express"),
aws = require("aws-sdk");
const athenaExpressConfig = {
aws,
db: "messaging",
getStats: true
};
const athenaExpress = new AthenaExpress(athenaExpressConfig);
exports.handler = async event => {
const sqlQuery = "SELECT * FROM delivery_receipts LIMIT 3";
try {
let results = await athenaExpress.query(sqlQuery);
return results;
} catch (error) {
return error;
}
};

Iterating through list and make sequential network calls

How do i iterate through a list and make sequential network calls using a sdk?
I am trying to use Coinbase's Node sdk and get the first 10 transactions for all accounts.
I have a list of accounts, and i iterating through them and calling client.account, client.transactions, and client.transactions(pagination). And im adding the results to an array of transactions and returning that array.
I couldn't get this to work with async/await or request-promises.
Any ideas?
https://developers.coinbase.com/api/v2#transactions
var rp = require('request-promise');
var coinbase = require('coinbase');
var client = new coinbase.Client({ 'apiKey': 'keyStuff', 'apiSecret': 'secretStuff' });
var accountList = ['acct1','acct2','acct3',];
var transactionList = [];
try {
let ps = [];
accountList.forEach(acctId => {
var account = client.getAccount(accountId, null);
ps.push(rp(account));
});
Promise.all(ps)
.then(responses => {
for (var i = 0; i < responses.length; i++) {
var result = responses[i];
rp(result.account.getTransactions(null))
.then(res => {
res.pagination = 10;
return rp(result.account.getTransactions(null, res.pagination));
}).catch(err => console.log(err))
.then(txns => {
try {
if (txns.length > 0) {
txns.forEach(function(txn) {
var transaction = {
"trade_type": "",
"price": ""
};
transaction.trade_type = txn.type;
transaction.price = txn.native_amount.amount;
transactionList.push(transaction);
});
}
}
catch (err) {
console.log(err);
}
});
}
}).catch(err => console.log(err));
return transactionList;
//-------------------------------------------------------------------
// if (accountList.length > 0) {
// for (let acctId of accountList) {
// console.log("account id: " + acctId);
// await delayTransactions(acctId);
// }
// console.log("got here last");
// return transactionList;
// }
}
catch (error) {
console.log(error);
}
The commented-out delay method has nested async calls like this:
await client.getAccount(accountId, async function(err, account) {
if (err) {
console.log(err);
}
else {
await account.getTransactions(null, async function(err, txns, pagination) {
.
.
.
Solved it by using async/await and promises. awaiting the coinbase methods wouldn't work because they aren't async functions (surprise!).
function delayedMethod() {
new Promise(resolve => {
client.getAccount(accountId, async function(err, account) {
if (err) {
console.log(err);
}
else {
account.getTransactions(null, async function(err, txns, pagination) {
.
.
.
resolve();
});
}

Categories

Resources