How to return promise value JS - javascript

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

Related

NodeJs wait until all calls in loop are finished

I have the following code in node (beginner), I want to call a function after all the code has been executed in the loop, I know that I have to use promises, but I don't know how to apply them in this situation. The code has been simplified. Many thanks.
const axios = require('axios').default;
axios.post('url', {
params: {}
}).then(response=>{
var objResponse = response.data
Object.keys(objResponse).forEach(function (key){
axios.post('url', {
params: {}
}).then(response=>{
var n=0;
getCode(n);
})
})
**finishFunction**()
})
function getCode(n) {
axios.post('url', {
params: {}
}).then(response=>{
if(response.data){
if (response.data.code) {
getData();
}else{
if (n < 10) {
n++;
getCode(n);
} else {
getTimeout();
}
}
}
})
}
function getTimeout() {
axios.post('url', {
params: {}
})
}
function getData() {
axios.post('url', {
params: {}
}).then(response=>{
console.log('finished one loop');
})
}
Best way to achieve what you want is to use async/await with a regular for-loop.
Here is an example of what I mean. You can adjust it to your needs:
async doSomeStuff() {
const results = []
const res1 = await axios.post('url', {
params: {}
})
// assuming res1 is an Array
for (let i=0; i < res1.length; i++) {
const result = await axios.post('url2', {
params: {}
})
results.push(result)
}
return results
}
You can also call other async functions inside the loop as you are doing.
You could use Promise all and map together, alongside async/await
async function myFunction(){
const result = await axios.post('url');
const keys = Object.keys(result.data);
const requests = keys.map((key, i) => axios.post('url')/*chain more if u need*/)
const allResults = await Promise.all(requests);
// All done run your function below
}
If you are happy for each item in the loop to be run at the same time, you can use an array of promises and wait for everything in the array to finish. Then you just have to add promises to your functions which will take an unknown amount of time to run. Something like this might get you started:
const axios = require('axios').default;
axios.post('url', {
params: {}
}).then(response=>{
var objResponse = response.data
var proms = [];
Object.keys(objResponse).forEach(function (key){
proms.push(
axios.post('url', {
params: {}
}).then(response=>{
var n=0;
return getCode(n);
})
)
})
var items = Promise.all(proms);
items.then(function (results) {
// **finishFunction**()
});
})
function getCode(n) {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
}).then(response=>{
if(response.data){
if (response.data.code) {
getData();
}else{
if (n < 10) {
n++;
getCode(n).then(data => {
return resolve(data)
});
} else {
getTimeout().then(data => {
return resolve(data)
});
}
}
}
})
})
}
function getTimeout() {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
})
return resolve()
})
}
function getData() {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
}).then(response=>{
console.log('finished one loop');
return resolve(response)
})
})
}

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

https requests in loop, call function after last request has been made node

Assuming I am calling https a multiple times to retrieve data, and I want to call a function formatJsonToLocale at the end of the last request. Is there a way to determine when that request has ended, other than checking for the last element of the array.
let sheetsArray = []
function sheetsAsJsonById (ids) {
for (let i = 0; i < ids.length; i++) {
const queryUrl = `sheets.html`
https
.get(queryUrl, res => {
let stream = []
res
.on('data', function (data) {
stream.push(data)
})
.on('end', function () {
let data = Buffer.concat(stream)
data = JSON.parse(data)
sheetArrays.push(data['values'])
formatJsonToLocale(sheetsArray) // <----- call this one after last request
})
})
.on('error', err => {
console.error(`Error in response: ${err}`)
})
}
}
when I call formatJsonToLocale outside of the function I will have the problem that the former function might not be finished as https handles stuff asynchronously.
Any suggestions on how to handle this?
You would need to keep track of execution of async code (https.get) that is getting executed within for loop. This can be achieved using promises as below:
let sheetsArray = []
function sheetsAsJsonById (ids) {
let promises = []
for (let i = 0; i < ids.length; i++) {
const queryUrl = `sheets.html`
promises.push(makeHTTPRequest(queryUrl))
}
Promise.all(promises).then((sheetArrays) => {
formatJsonToLocale(sheetsArray)
})
}
const makeHTTPRequest = (url) => {
return new Promise((resolve, reject) => {
https
.get(url, res => {
let stream = []
res
.on('data', function (data) {
stream.push(data)
})
.on('end', function () {
let data = Buffer.concat(stream)
data = JSON.parse(data)
resolve(data)
})
.on('error', err => {
console.error(`Error in response: ${err}`)
})
})
}
If you want to stick to callbacks you could use async.each function of async node module.
Wrap https.get in a Promise, which resolves on the end event, and rejects on any error. Now you can await the promise, and call the function once the for loop is done
let sheetsArray = []
function sheetsAsJsonById(ids) {
for (let i = 0; i < ids.length; i++) {
const queryUrl = `sheets.html`
await new Promise((resolve, reject) => {
https
.get(queryUrl, res => {
let stream = []
res
.on('data', function(data) {
stream.push(data)
})
.on('end', function() {
let data = Buffer.concat(stream)
data = JSON.parse(data)
sheetsArray.push(data['values'])
resolve();
})
})
.on('error', err => {
reject(err);
})
})
}
formatJsonToLocale(sheetsArray)
}

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

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

Categories