Nodejs How To Iterate With a Promise - javascript

I'm trying to iterate through an array of AD users and return some user information.
I've been looking for a few hours now, or more and haven't been quite able to get my head around the async nature of the activedirectory2 npm package.
I'm getting part of the result I need, however when iterating through the list of usernames, I'm only getting the first one printing out to console.
getADUser.js:
var ActiveDirectory = require('activedirectory2');
var config = require('../../conf/conf-ad.json')
var fileTime = require('./w32FiletimeToEpoch')
var moment = require('moment')
// Find user, return all
var ad = new ActiveDirectory(config);
var getADUser = function (sAMAccountName, opts) {
return new Promise(function (resolve, reject) {
ad.findUser(opts, sAMAccountName, function (err, user) {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
// return;
}
if (!user) {
console.log('User: ' + sAMAccountName + ' not found.');
} else {
if (user.userAccountControl == 514) {
user.userAccountControl = 'Disabled'
} else {
user.userAccountControl = 'Active'
}
if (user.pwdLastSet) {
user.pwdLastSet = `${moment(fileTime(user.pwdLastSet))} - ${moment(fileTime(user.pwdLastSet)).fromNow()}`
}
if (user.lastLogonTimestamp) {
user.lastLogonTimestamp = `${moment(fileTime(user.lastLogonTimestamp))} - ${moment(fileTime(user.lastLogonTimestamp)).fromNow()}`
}
if (user.lastLogon) {
user.lastLogon = `${moment(fileTime(user.lastLogon))} - ${moment(fileTime(user.lastLogon)).fromNow()}`
}
// return;
// return user.promise();
// console.log(user)
// test.push(user)
resolve(JSON.stringify(user));
}
});
})
}
module.exports = getADUser
checkADCompletions.js:
var checks = ['USERONE', 'USERTWO']
let opts = {
attributes: ['sAMAccountName', 'userAccountControl']
};
let checkADCompletions = function (userList) {
let data = []
return new Promise(function (resolve, reject) {
return new Promise(function (res, rej) {
for (let i = 0; i < userList.length; i++) {
getADUser(userList[i], opts)
.then(function (s) {
data.push(s)
}).then(function () {
resolve(data)
})
}
})
})
}
checkADCompletions(checks).then(function (d) {
console.log(d) \\ Only prints the first user details
})

You can use Promise.all like this:
let checkADCompletions = function (userList) {
var promises = userList.map(function (user) {
return getADUser(user, opts);
})
return Promise.all(promises);
}
You are basically creating an array of promises and then executing them all concurrently.
And then use it like so:
checkADCompletions(checks)
.then(function (responses) {
console.log(responses); // this will be an array
})
.catch(function (err) {
// if any of the promises fail, it will enter here.
// err will be the value of the rejected promise
})
Promise.all will fail even if just one of the checked users fail. So, you need to handle errors nicely, i.e. deal with any possible outcome of ad.findUser:
var getADUser = function (sAMAccountName, opts) {
return new Promise(function (resolve, reject) {
ad.findUser(opts, sAMAccountName, function (err, user) {
if (err || user == null) {
console.log('ERROR: ' + JSON.stringify(err));
reject(err);
}
if (user.userAccountControl == 514) {
user.userAccountControl = 'Disabled'
} else {
user.userAccountControl = 'Active'
}
if (user.pwdLastSet) {
user.pwdLastSet = `${moment(fileTime(user.pwdLastSet))} - ${moment(fileTime(user.pwdLastSet)).fromNow()}`
}
if (user.lastLogonTimestamp) {
user.lastLogonTimestamp = `${moment(fileTime(user.lastLogonTimestamp))} - ${moment(fileTime(user.lastLogonTimestamp)).fromNow()}`
}
if (user.lastLogon) {
user.lastLogon = `${moment(fileTime(user.lastLogon))} - ${moment(fileTime(user.lastLogon)).fromNow()}`
}
resolve(user);
});
})
}

A simple fix would be to call resolve only when the for loop is finished.
// checkADCompletions.js
var checks = ['USERONE', 'USERTWO']
let opts = {
attributes: ['sAMAccountName', 'userAccountControl']
};
let checkADCompletions = function (userList) {
let data = []
return new Promise(function (resolve, reject) {
for (let i = 0; i < userList.length; i++) {
getADUser(userList[i], opts)
.then(function (s) {
data.push(s)
}).then(function () {
if (i === userList.length) {
resolve(data)
}
})
}
})
})
}
checkADCompletions(checks).then(function (d) {
console.log(d)
})

When you call resolve, you close out the Promise. You're initiating two Promises and then a for loop, and you call resolve inside the for loop. So the first user gets populated but the for loop does not continue because the Promise has finished executing. Move resolve outside of the loop and you should be good to go.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

Related

How to return promise value JS

I'm trying to return a value obtained in a promise statement and I'm getting continuously Promise { undefined }, someone knows how to do it correctly?
When I render the .ejs file with the data it get's anything.
// /documents/vocabulary file
async function transformar() {
return new Promise(resolve => {
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
if (data) {
var d = data.split('\n');
var a = []
for (let i=0; i<d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
a.sort(dynamicSort('eng'));
resolve(a);
}
});
});
}
//index.js file
const vocabulary = require('./documents/vocabulary');
const myFunction = async () => {
const data = await vocabulary.transformar();
return data;
}
const vocab = myFunction();
console.log(vocab)
app.use('/', function(req,res){
res.render(path.join(__dirname+'/Vocabulary.ejs'), {vocabulary_data: vocab});
});
Thanks for reading!
First of all, your transformar() method does not actually return a promise. Make sure you do that first:
function transformar() {
return new Promise((resolve, reject) => {
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
if (data) {
var d = data.split('\n');
var a = []
for (let i=0; i<d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
a.sort(dynamicSort('eng'));
resolve(a);
} else {
reject();
}
});
});
}
Here are some suggestions:
You don't need to use async, since you already return a promise in the method now
All code paths should end up in reject() or resolve(), to avoid memory leaks (like what Thomas suggested in the comment below)
Now, back to the issue of getting data out of your promise: as there is no support for top-level await, so you need to either chain a .then() from the returned promise:
vocabulary.transformar().then((data) => console.log('data', data));
... or use await in an IIFE:
(async () => {
const data = await vocabulary.transformar();
console.log('data', data);
})();
If the Promise is being executed in a function, then you can simply use await:
const myFunction = async () => {
const data = await vocabulary.transformar();
console.log('data', data);
}
myFunction();
You should try this code :
async transformar() {
return new Promise((resolve, reject) => {
console.log('Sorting data');
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', async (error, data) => {
if (data) {
//format data on array-dict
var d = data.split('\n');
var a = []
for (let i = 0; i < d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
//sort data
await a.sort(dynamicSort('eng'));
resolve(a);
}
});
})
}
var data;
vocabulary.transformar().then(result=>{
data = result;
console.log('data', data);
});
Your asynchronous function isn't returning anything. Also, the fs.readFile function doesn't return promises, it takes a callback. You should wrap it within a promise and resolve it with your final value.
function transformar() {
return new Promise(function (resolve, reject) {
fs.readFile(
'src/documents/Vocabulary2.txt',
'utf8',
function (error, data) {
if (data) {
//format data on array-dict
var d = data.split('\n');
var a = [];
for (let i = 0; i < d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim(),
});
}
//sort data
a.sort(dynamicSort('eng'));
resolve(a);
} else {
reject(new Error('No data was found.'));
}
}
);
});
}
transformar()
.then(function (data) {
console.log(data);
})
.catch(function (err) {
console.error(err);
});

How to recover a result of query mongodb outside of its function?

I am a new javascript user and I am looking for a rescuer the result (count) of a memory request.
More precisely, maybe you can comment, put my variable in my conv object
right now my result is [promised object]
I tried with async and promised javascript but I do not understand the logic yet
Thank you so much,
Gabriel
var conversations = []
for (var i = 0; i < conversation.length; i++) {
for (var j = 0; j < conversation[i].users.length; j++) {
conv = conversation[i].users[j]
async function asyncCall() {
function countUnread() {
filterUnread = {$and:[
{toID: user.properties.chat.id},
{fromID: conversation[i].users[j].id},
{ read: false }]}
return new Promise(resolve => {
Message.countDocuments(filterUnread, function (err, count) {
resolve(count)
})
})
}
var count = await countUnread();
console.log(count);
console.log(conv)
resolve(!!!!count!!!!) ;
}
asyncCall();
conv.unread = !!!!I_want_my_count_here!!!!
conversations.push(conv);
resolve(conversations)
}
}
Mixing async functions and Promise constructors makes the code hard to track and causes problems like this one. Instead only wrap the most low level parts into Promise constructors, let tuem resolve to something useful, then compose multiple such Promises using async :
const filter = { users: { $elemMatch: { id: user.properties.chat.id } } }
function getConversations(filter) {
return new Promise(function(resolve, reject) {
Conversation.find(filter, function(err, conversations) {
if(err) reject(err) else resolve(conversations);
});
});
}
function countUnread(user) {
const filterUnread = { $and:[
{toID: user.properties.chat.id},
{fromID: user.id},
{ read: false }
]};
return new Promise((resolve, reject) => {
Message.countDocuments(filterUnread, function (err, count) {
if(err) reject(err) else resolve(count);
});
});
}
async function composeConversations() {
const conversations = await getConversations();
const result = [];
for(const conversation of conversations) {
for(const user of conversation.users) {
const count = await countUnread(user);
user.unread = count;
}
result.push(conversation);
}
return result;
}

Promises when passing a function as a parameter

I understand how promises work for the most part, but I have a lot of trouble understanding how to deal with them when I need to pass a function as a parameter:
var promise = new Promise(function(resolve, reject) {
// Do async job
ec2.describeInstances(function(err, data) {
console.log("\nIn describe instances:\n");
var list = [];
if (err) reject(err); // an error occurred
else {
var i = 0 ;
//console.log(data.Reservations);
var reservations = data.Reservations;
for (var i in reservations) {
var instances = reservations[i]['Instances'];
var j = 0;
//console.log(JSON.stringify(instances, null, 2));
for (j in instances){
var tags = instances[j]
var k = 0;
var instanceId = tags['InstanceId'];
var tag = tags['Tags'];
var l;
//console.log(tag);
for (l in tag){
//console.log(instanceId);
//console.log(tag[l]['Value']);
if (String(tag[l]['Value']) == '2018-10-15T23:45' || String(tag[l]['Key']) == 'killdate') {
console.log(tag[l]['Key'] + ' ' + tag[l]['Value']);
list.push(instanceId);
console.log(list);
//return(list);
}
}
}
}
resolve(list);
}
});
});
promise.then(function (list) {
ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
});
before I had the first part of the code as:
return new Promise(function(resolve, reject) { ... }
and that worked for the first part, but as soon as I changed it to a "var" and added the new promise in underneath, it stopped working. (edit) When I mean "stopped working" I mean, neither of the two functions run, i.e.: it ends the handler before either functions are finished and none of the return statements or console logs.
Any help would be greatly appreciated!
Thanks!
Wondering if something like this would work:
var promise = Promise.resolve(function() {
return ec2.describeInstances...
})
promise
.then(/* handle successful promise resolution */ )
.catch(/* handle promise rejection */ )
var promise = Promise.resolve();
promise
.then(function() {
return ec2.describeInstances(function(err, data) {
var list = [];
if (err) throw err; // an error occurred
// else logic
})
})
.catch(/* if needed here */)
.then(function (list) {
return ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
})
.catch(/* if needed here */)
my suggestion is to break up your logic - it will be easier to handle the result you want to achieve.
A proper way in my opinion:
promise function(a service function):
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
result = () => resolve(data);
fail = () => reject(err);
});
}
then your promise caller:
myAsyncFunction().then(dataHandler(result), // "promise worked!"
function (err) {// Error: "It broke"
console.log(err)
});
then the logic:
function dataHandler(data) { /* data logic */}
good luck
I ended up fixing it. Sorry, forgot to post back before I added in the SNS portion. I ended up learning a ton about functional programming on the way and decided to use the await function over the complicated promise syntax. Below is the code:
exports.handler = async (event, result, callback) => {
const AWS = require('aws-sdk');
const date = new Date().toISOString().substr(0, 16)
const ec2 = new AWS.EC2();
var sns = new AWS.SNS();
console.log("date is: " + date)
console.log(date.length);
const params = {
TopicArn:'arn:aws:sns:us-east-1:503782973686:test',
Message:'Success!!! ',
Subject: 'TestSNS'
}
const describeResult = await ec2.describeInstances().promise()
const terminatableInstances = await describeResult
.Reservations
.reduce((acc, reservation) => acc.concat(reservation.Instances), [])
//'2018-10-15T23:45'
.map((instance) => {
//console.log(instance)
//console.log(instance.Tags)
var date = instance.Tags
.filter(tag => tag.Key == 'killdate' && tag.Value == date) //date should be in this format on tag: 2018-10-15T23:45
.reduce((acc, curr) => curr.Value, null);
if (date != null) {
return instance.InstanceId;
}
return null;
})
.filter(id => id != null)
console.log(terminatableInstances);
const snsPush = await ec2.terminateInstances({
InstanceIds: terminatableInstances,
//DryRun: true //set this flag if you want to do a dry run without terming instances
}, (err, data) => {
if (err) {
console.log('no instances to terminate!')
}
else {
console.log('terminated instances')
}
})
console.log(snsPush)
//return(snsPush).promise()
return sns.publish(params, (err, data) => {
if (err) {
console.log(err, err.stack);
}
else {
console.log('sent');
}
}).promise();
};

issue with async await

I am trying to make two block of code to run sequentially with async and await
I have the code in stackblitz, open the console on Chrome to see the trace
let isMomHappy = true;
function willIgotNewPhone () {
return new Promise( (resolve, reject) => {
if (isMomHappy) {
const phone = {
brand: 'samsung',
color : 'black'
};
resolve(phone);
} else {
const reason = new Error('mom not happy');
reject(reason);
}
});
}
async function showOff(phone) {
return new Promise( (resolve, reject) => {
const message = ' Hey Friend I show my phone ' + phone.brand;
resolve(message);
});
}
async function askMom() {
return await new Promise( async (resolve) => {
console.log('before asking Mom'); // log before
await willIgotNewPhone()
.then( function(fullfilled) {
console.log('Got phone from mom ' + JSON.stringify(fullfilled));
})
.catch( function(error) {
console.log(error.message);
});
console.log('after asking Mom'); // afeter log
resolve('END');
});
}
let data: any[] = [];
async function createData() {
return new Promise( (resolve, reject) => {
for (let index = 0; index < 500000; index++) {
const element: any = {};
element.id = index;
element.name = '' + index;
data.push(element);
}
if (data && data.length > 0) {
console.log(' ====== creating data size=%s', data.length);
resolve(data);
} else {
reject( new Error(' ==== Creating data Error : empty'));
}
});
}
async function callCreateData() {
return new Promise( async (resolve) => {
console.log(' ======before creating data');
await createData().then( (dataReturn: any[]) => {
console.log(' ----datareturn length=%s', dataReturn.length);
});
console.log(' ======after creating data');
});
}
async function callingMom() {
await askMom().then( (str) => {
console.log(str);
});
}
callingMom();
callCreateData();
I am calling 2 functions this.callingMom() and this.callCreateData();
and expecting the traces of the 2 functions to be sequential
I was expecting the following output
before asking Mom
Got phone from mom {"brand":"samsung","color":"black"}
after asking Mom
=====before creating creating data
===== creating data size=500000
----datareturn length=500000
===== after creating creating data
but I got the output :
before asking Mom
======before creating data
====== creating data size=500000
Got phone from mom {"brand":"samsung","color":"black"}
----datareturn length=500000
======after creating data
after asking Mom
END
Any idea what is my problem ?
Thanks
async functions can be used for two things, primarilly: to return a Promise, and to be able to use the await keyword inside. If you're not using await, or if the only await you're using is the Promise that's going to be returned, there's no point in having an async function at all - just use a normal function that returns a Promise, like in your willIgotNewPhone.
Also, in order to chain promises together, you need to use then. Simply calling asynchronous functions right after the other won't cause the thread to pause until they're done. Like this:
callingMom().then(callCreateData);
let isMomHappy = true;
function willIgotNewPhone() {
return new Promise((resolve, reject) => {
if (isMomHappy) {
const phone = {
brand: 'samsung',
color: 'black'
};
resolve(phone);
} else {
const reason = new Error('mom not happy');
reject(reason);
}
});
}
function showOff(phone) {
return new Promise((resolve, reject) => {
const message = ' Hey Friend I show my phone ' + phone.brand;
resolve(message);
});
}
function askMom() {
return new Promise(async (resolve) => {
console.log('before asking Mom'); // log before
await willIgotNewPhone()
.then(function(fullfilled) {
console.log('Got phone from mom ' + JSON.stringify(fullfilled));
})
.catch(function(error) {
console.log(error.message);
});
console.log('after asking Mom'); // afeter log
resolve('END');
});
}
let data = [];
function createData() {
return new Promise((resolve, reject) => {
for (let index = 0; index < 500000; index++) {
const element = {};
element.id = index;
element.name = '' + index;
data.push(element);
}
if (data && data.length > 0) {
console.log(' ====== creating data size=%s', data.length);
resolve(data);
} else {
reject(new Error(' ==== Creating data Error : empty'));
}
});
}
function callCreateData() {
return new Promise(async (resolve) => {
console.log(' ======before creating data');
await createData().then((dataReturn) => {
console.log(' ----datareturn length=%s', dataReturn.length);
});
console.log(' ======after creating data');
});
}
function callingMom() {
return askMom().then((str) => {
console.log(str);
});
}
callingMom().then(callCreateData);

How to make this piece of code asynchronous via promises?

Any Glue. Please do not give example of SETTIMEOUT.
My supervisor said that it is unnecessary having more then() statement if the then()'s do not return any new promise.I could not get it completely, I have looked at Exploring ES6 but there is no very well example of my situation.
let readData = new Promise(function (resolve, reject) {
fs.readFile('/home/geek/Desktop/activity-logs.csv', 'utf8', (err, data) => {
if (err)
reject();
resolve(data);
});
});
readData
.then((data) => {
bankLogs = splitString(data, ';')
})
.then(() => {
for (let index = 2; index < bankLogs.length; index += 5) {
objectOfUsers[bankLogs[index]] = {}
temporarySymbols.push(bankLogs[index].concat(bankLogs[index + 1]));
}
Object.keys(objectOfUsers).forEach(function (element) {
objectKeys.push(element)
});
for (let index = 0; index < objectKeys.length; index++)
createObject(index);
console.log(objectOfUsers)
})
.catch((err) => console.log('Error happened : ' + err));
From your code snippet you implemented Promise chain where resolving one promise initializes and returns new one.
Something like:
let readData = new Promise(/* ... */);
readData.then((data) => {
let promise2 = new Promise(/* ... */);
return promise2;
}).then(() => {
// ...
})
.catch((err) => console.log('Error happened : ' + err));
However after resolving readData you only wrote simple logic and there is no new promises: bankLogs = splitString(data, ';')
So It should be:
let readData = new Promise(function (resolve, reject) {
fs.readFile('/home/geek/Desktop/activity-logs.csv', 'utf8', (err, data) => {
if (err)
reject();
resolve(data);
});
});
readData.then((data) => {
var bankLogs = splitString(data, ';')
for (let index = 2; index < bankLogs.length; index += 5) {
objectOfUsers[bankLogs[index]] = {}
temporarySymbols.push(bankLogs[index].concat(bankLogs[index + 1]));
}
Object.keys(objectOfUsers).forEach(function (element) {
objectKeys.push(element)
});
for (let index = 0; index < objectKeys.length; index++)
createObject(index);
console.log(objectOfUsers)
})
.catch((err) => console.log('Error happened : ' + err));
If you use bankLogs inside Promise only, make it private.
Promise chain approach we use mostly for good code readability and handling one error callback for all promises.
Edit
You can pass value through Promise chain by using some wrapper object or list of items (example):
'use strict';
var fs = require('fs');
let readData = new Promise(function (resolve, reject) {
fs.readFile('/Users/maxim/Appsflyer/projects/ZEVEL/file1.txt', 'utf8', (err, data) => {
if (err)
reject();
var obj = {
data: data,
bankLogs: {} // in 1st Promise initialize bankLogs
};
resolve(obj);
});
});
// 2nd dummy Promise
let newReadData = new Promise(function (resolve, reject) {
fs.readFile('/Users/maxim/Appsflyer/projects/ZEVEL/file2.txt', 'utf8', (err, data) => {
if (err)
reject();
resolve(data);
});
});
readData.then((obj1) => {
obj1.bankLogs.value = "someValue";
return newReadData.then(function(data2){
obj1.data2 = data2;
return obj1;
});
}).then((dataTotal) => {
console.log(dataTotal);
})
.catch((err) => console.log('Error happened : ' + err));

Categories