Promises when passing a function as a parameter - javascript

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

Related

Print result from promise map

Hi guys I have 2 methods.
checkVenueAvailability(venues) {
var replaced = venues.replace(/\t/g, "");
var venues = replaced.split(',');
var length = venues.length;
Promise.all(venues.map(venue => {
return new Promise((resolve, reject) => {
pool.query("SELECT * FROM peminjaman_venue WHERE nama_venue = ?", venue,
function (err, rows, fields) {
if (err) {
return reject(err);
}
return resolve(rows);
})
})
}))
}
And
mengajukan_event(req, res) {
helper.checkVenueAvailability(req.body.venue_1)
.then(function (result) {
console.log(result);
}).catch(function (err) {
console.log(err);
})
}
I want to print the result of checkVenueAvailability in mengajukan_event. How to achieve that. My code above just return error. Thankyou.
You aren't returning anything. Just return the promise from Promise.all:
return Promise.all(venues.map(venue => {
// ^^^^^^
Also note that there are DB adapters with a Promise-based API available now. Or, if you can't use one, you can use util.promisify.
const promiseQuery = util.promisify(pool.query);
Then
checkVenueAvailability(venues) {
var replaced = venues.replace(/\t/g, "");
var venues = replaced.split(',');
return Promise.all(venues.map(venue => promiseQuery("SELECT * FROM peminjaman_venue WHERE nama_venue = ?", venue));
}
(I also removed var length = venues.length; since length wasn't used for anything.)

Nodejs How To Iterate With a Promise

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

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

Async promises inside loop

I know this is a topic where there is a lot of info, but I still can't solve this.
I am working with NodeJs that connects to a mysql server, I have an array, I need to iterate the array and do some stuff with the data, the thing is I need to do a query to the mysql server but I need to the for loop wait until I got the results of the query, I have tried a lot of things, now I am trying with .each method of bluebird but still is not working properly, this is my code.
the initial function is Notification.getByUser
thanks in advance
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
if (result.length === 0) {
resolve(false);
} else {
resolve(true);
}
});
});
};
Notifications.isMatching = function(item, user, type) {
return new Promise((resolve, reject) => {
console.log(type, item, user);
if (item !== null) {
if (item !== user) {
resolve(false);
}
}
resolve(true);
});
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState, cb) {
var where = { status: 1 };
var plainText = '';
var items = Notifications.find({ where });
Promise.each(items, item => {
return Notifications.isMatching(item.isZolver, isZolver, 'isZolver')
.then(() => {
Notifications.isMatching(item.cityId, cityId, 'cityId');
})
.then(() => {
if(item.extraCondition !== null && item.extraCondition !== '') {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// console.log(sql);
Notifications.execute(sql, item.id)
.then(render => console.log('extraCondition', render));
} else {
console.log('extraCondition', true);
}
});
}).then(res => {
// console.log('res del loop', res);
});
cb(null, 'ok');
};
};
There are several issues with your code:
In order to chain promises, you must make sure to return promises that you create within a then callback
You should not need to create a promise for results that are immediately available (isMatching)
The then callback is always executed when a promise fulfils. It does not matter whether you do resolve(false): even if the promised value is false this will still make the promise fulfilled and trigger the then callback(s).
There are some unknowns in your code, like Notifications.find, and what kind of SQL you are executing: does it return a result set, if so, would you not be interested to get that result instead of just resolving to a boolean?
Anyway, here are some applied corrections:
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
resolve (result.length !== 0); // The `if ... else` is overkill
});
});
};
//You don't need isMatching to return a promise
Notifications.isMatching = function(a, b) {
return a === null || a === b;
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState) {
var where = { status: 1 };
var items = Notifications.find({ where })
// use filter() to limit the items to those you want to execute the SQL for
.filter(item => {
return Notifications.isMatching(item.isZolver, isZolver)
&& Notifications.isMatching(item.cityId, cityId)
&& item.extraCondition !== ''
});
// Return the promise! (don't go back to the old callback system!)
return Promise.each(items, item => {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// Return the promise!
return Notifications.execute(sql, item.id);
}).then(res => {
console.log('res del loop', res);
});
};
};
Note that the signature of Notifications.getByUser does not have the final callback parameter: you should keep using promises once you start with them, so when you call this function, call the result's then method like you would do for any promise:
Notifications.getByUser( .......arguments....).then( function () {
// do something...
});

nodejs use gm module to process the images but I need it sync

here are two method .but i need it to finish sync.But it is failed.
I want to use the Promise.all to keep it sync but i find the images process is async and it can't reject or resolve in a loop! It really makes me confused .
below is my code.I can't catch the res or err. it just end after processed the images...even don't excute the test Method,but i wonder that Promise.all is a sync method? And is there any way to catch the res or err information? Thanks !
var gm = require('gm')
var fs = require('fs')
var config = require('./imgConfig')
var image = ['1.jpg', '2.jpg', '3.jpg', '4.jpg','5.jpg','6.jpg']
var _image = ['_1.jpg', '_2.jpg', '_3.jpg', '_4.jpg','_5.jpg','6.jpg']
testS()
function testS() {
uploadFiles(image, _image, 'subModule').then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
}
function uploadFiles(inputArray, outputArray, type) {
return Promise.all([multipleResize(inputArray, outputArray, type), test()])
}
function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("object");
resolve("yeah")
}, 100)
})
}
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
imgConfig
module.exports = {
'subModule': {
width: 300,
height: 300
},
inputPath:'./input/',
outputPath:'./output/'
}
In multipleResolve, you are running return resolve() as part of the #forEach() call. This probably isn't what you want, as the first iteration will resolve the promise you returned, even though all of your gm .write() calls won't be finished yet.
I would consider doing something like this -- note the internal use of Promise.all() and the inputArray#map() to convert each img/index pair to their own Promise. I also swapped around some of the error logic to make sure the errors are checked as soon as possible.
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
var flag = 0;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
flag++;
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
Side note, you seem to be using promises (mostly) correctly, but you are incorrectly calling them sync, when they typically aren't synchronous.

Categories