How do i set the response of a promise to a variable - javascript

I have an async function that returns a value
async getUsername(env:string,skuName: string,token:string): Promise<any> {
let value = await this.getDeviceName(env,skuName,token);
return value;
};
in another function, I am calling this get username function like this
let productN;
const prod = this.getUsername(env,skuName,token).then((resp) => {
productN= resp;
this.wait(300);
});
the variable productN is working, as i am able to see the response in my log, but when i try to use productN outside of this block, i am running into undefined.
promotion = {
name: productN,
startDate: start,
endDate: end
};
I am trying to set name to productN, and i just can not get it to work.
Can anyone explain to me what i am doing wrong please? Thanks

You can either assign to the promotion when you receive the response -
const prod = this.getUsername(env,skuName,token).then((resp) => {
productN = resp;
promotion.name = resp
this.wait(300);
});
Since your getUsername is asynchronous, you can wait for the response using await and then assign to promotion object.
(async() => {
const resp = await this.getUsername(env,skuName,token);
promotion = {
name: resp,
startDate: start,
endDate: end
}
})();
--- Edit ---
const input = [
{
env: 'lorem',
skuname: 'skuname1',
token: 'dummy'
},
{
env: 'lorem',
skuname: 'skuname2',
token: 'dummy'
}
];
const getUserName = (username) => {
return new Promise(resolve => {
setTimeout(()=>resolve(username), 2000);
});
};
(async () => {
const resp = await Promise.all(input.map(({skuname}) => getUserName(skuname)));
console.log(resp);
})();
// Or
Promise.all(input.map(({skuname}) => getUserName(skuname)))
.then(resp => {
console.log(resp);
});

try
const productN = await this.getUsername(env,skuName,token);
console.log(productN);

Can you try doing this? Return your getDeviceName function in getUsername;
getUsername(env:string, skuName: string, token:string): Promise<any> {
return this.getDeviceName(env, skuName, token);
};
const productN = await this.getUsername(env, skuName, token);
console.log(productN);
I don't know why you use getUsername. since you can get the value in productN directly from getDeviceName.
like
const productN = await this.getDeviceName(env, skuName, token);
If you want to do any other things inside getUsername. you can make it return a promise.
getUsername(env:string, skuName: string, token:string): Promise<any> {
return New Promise(async (resolve,reject)=>{
try {
let value = await this.getDeviceName(env,skuName,token);
...
//all other things you want to do here
...
resolve(value);
} catch (error) {
reject(error)
}
}
};
const productN = await this.getUsername(env, skuName, token);
console.log(productN);
nb: I used try catch block for error handling, Ignore it if you don't want to.

Related

how to put await in then portion under Promise.all

In the following code, I used Promise.all to enable async called when calling map, but in the then portion, i need another call to await, the syntax does not allow me to do that, what's the way to enable await under then branch? Alternatively, is there more elegant way to achieve the same purpose of my code without introducing so many different constructs like Promise.all?
const handleSend = async ({ text, attachments }) => {
const attachmentsArr = null;
Promise.all(attachments.map(async a => {//<--use Promise all for async within map
const isImage = a.type.startsWith('image/');
let response = null;
if (isImage) { response = await channel.sendImage(a); } else { response = await channel.sendFile(a); }
return {
name: a.name,
path: response.file,
size: a.size,
isImage
};
})).then(attachmentsArr => {
await channel.sendMessage({ //<--can't use await here
text,
workspaceId: '1234567',
attachments: attachmentsArr
});
}
);
};
I would write it like this
const handleSend = async ({ text, attachments }) => {
const promisesArray = attachments.map(async a => {
const isImage = a.type.startsWith('image/');
let response = null;
if (isImage) {
response = await channel.sendImage(a);
} else {
response = await channel.sendFile(a);
}
return {
name: a.name,
path: response.file,
size: a.size,
isImage
};
});
const attachmentsArr = await Promise.all(promisesArray);
await channel.sendMessage({
text,
workspaceId: '1234567',
attachments: attachmentsArr
});
};
But, you should check attachmentsArr and see how to know when is empty to send null.
Mixing async/await with then can be confusing. You can instead write it all in async/await syntax like Braian's answer, or in then syntax like this:
const handleSend = ({ text, attachments }) => {
Promise.all(attachments.map(async a => {
const isImage = a.type.startsWith('image/');
let response = null;
if (isImage) { response = await channel.sendImage(a); } else { response = await channel.sendFile(a); }
return {
name: a.name,
path: response.file,
size: a.size,
isImage
};
})).then(attachmentsArr => {
return channel.sendMessage({ // <--- Return a promise
text,
workspaceId: '1234567',
attachments: attachmentsArr
});
}).then((result) => { // <--- Operate on the resolved value
// Do something with result
});
};

Dealing with nested forEach in Node.js

I am working on something that needs nested foreach loops in order to process some data.
Intention is there is an array of ID's I need to look up, each ID related to a user and I just need to extract their names from the response of an API call. Service A has the list of ID's and then sends a HTTP GET request to service B for each ID (can't change this), which then responds with the correct info in the format of
{
success: true,
user: {
name: 'John Doe'
}
}
Code that doesn't work but is my current code
incidents.forEach((name) => {
foo = {
nameID: names
}
const asyncForEach = async (array, callback) => {
for(let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
const startMulti = async () => {
await asyncForEach(foo.nameID, async (obj, index) => {
await userController.userInfo(obj)
.then((userInfo) => {
foo.nameID[index] = userInfo.user.name
})
.then(() => {
foobarArray.push(foo)
})
})
return res.json({success: true, foobar: foobarArray})
}
startMulti()
})
Took the origional idea of nested foreach loops from this blog post which allowed me to do another before though this one won't work
https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404
edit show variables
let foo
let foobarArray = []
Added await to usercontroller now getting proper output but error message saying Cannot set headers after they are sent to the client
Names is coming from outside the code and is just needed where it is. Not sure how to explain it without explaining the project in whole/detail.
edit show code for usercontroller.userinfo
exports.userInfo = function(id) {
return new Promise((resolve, reject) => {
let user = {
_id: id
}
options.json = {user}
request.get('/userInfo', options, (err, response, body) => {
resolve(body)
})
})
}
This code work perfectly as expected - ie it sends request with proper payload and returns proper response.
Edit current code attempt
let foobarArray =[]
let names = []
let foo
for (const incident of incidents) {
foo = {
nameID: names
}
for (const id of incident.names) {
const userInfo = await userController.userInfo(id)
names.push(userInfo.user.name)
}
}
return res.json({success: true, fooreturned: foobarArray})
Error message caused by await in front of userController
SyntaxError: await is only valid in async function
edit attempt at making async function (I normally don't use async/await instead use promises)
Even after attempt code below it still gives the error message above - I had tried same code before I edited to show error message
exports.userInfo = async function(id) {
return new Promise((resolve, reject) => {
let user = {
_id: id
}
options.json = {user}
request.get('/userInfo', options, (err, response, body) => {
resolve(body)
})
})
}
Full code below except the userInfo function above which already is shown above.
exports.monitor = function(req, res, next) {
const fooID = req.params.id
let foobarArray =[]
let names = []
let foo
Incident.find({fooID})
.exec((err, incidents) => {
if(err) {
console.log(err)
return res.json({success: false, foobar: []})
}
if(incidents != []) {
for (const incident of incidents) {
foo = {
nameID: incident.foo[0].names
}
for (const id of foo.responded) {
const userInfo = await userController.userInfo(id)
names.push(userInfo.user.name)
}
}
return res.json({success: true, foobar: foobarArray})
}
})
}
That's pretty much the whole code except some logging lines I still have to add. I pretty much need the foobar: foobarArray to be an array of objects - foo - where nameID is the array of proper names not ID's. The proper names are fetched via the userController.userInfo where the ID is passed.
edit - New code after async and promisify - not sure I did the promisify correctly
exports.monitor = async function(req, res, next) {
const fooID = req.params.id
const incidents = await userController.incidentFind(fooID)
}
exports.incidentFind = async function(id) {
return new Promise((resolve, reject) => {
const sevenAgo = moment().subtract(7, 'days').toISOString()
let alertArray =[]
let names = []
let alert
Incident.find({monitorID, createdAt: {$gte: sevenAgo}})
.exec((err, incidents) => {
if(err) {
console.log(err)
return res.json({success: false, foobar: []})
}
if(incidents != []) {
for (const incident of incidents) {
foo = {
nameID: incident.foo[0].names
}
for (const id of foo.responded) {
const userInfo = await userController.userInfo(id)
names.push(userInfo.user.name)
}
}
return res.json({success: true, foobar: foobarArray})
}
})
})
}
Not sure what the actual controller monitor should contain. Bit lost
Error message
/home/me/Projects/app/incidents/controllers/users.js:100
const userInfo = await userController.userInfo(id)
^^^^^
SyntaxError: await is only valid in async function
Looks like the function should be async already (put async before function name).
I think you're looking for a simple
exports.monitor = async function(req, res, next) {
const fooID = req.params.id
const foobarArray = […]
const names = […]
try {
const incidents = await Incident.find({fooID}).exec()
for (const name of incidents) {
const foo = {
nameID: []
}
for (const userId of names) {
const userInfo = await userController.userInfo(userId)
foo.nameID.push(userInfo.user.name)
}
foobarArray.push(foo)
}
res.json({success: true, foobar: foobarArray})
} catch(err) {
console.log(err)
res.json({success: false, foobar: []})
}
}

module.exports returning undefined

I`m trying to make a translation function with IBM Watson API in "services/ibmWatson/index.js". I receive the response correctly, but when I return the response to the "IncidentController.js" it receives as undefined.
const LanguageTranslatorV3 = require('ibm-watson/language-translator/v3');
const { IamAuthenticator } = require('ibm-watson/auth');
module.exports = {
async translate(phrase, language) {
const languageTranslator = new LanguageTranslatorV3({
authenticator: new IamAuthenticator({ apikey: '<my_API_key>' }),
url: 'https://gateway.watsonplatform.net/language-translator/api/',
version: '2020-03-28',
});
await languageTranslator.translate(
{
text: phrase,
source: 'pt',
target: language
})
.then(response => {
if(response.status=200){
console.log(response.result.translations);
return(response.result.translations);
}
return (["error"]);
})
.catch(err => {
console.log('error: ', err);
return (["error"]);
});
}
}
In the above code the console.log(response.result.translations) returns correctly:
[ { translation: 'Dog run over.' },
{ translation: 'Castration' },
{ translation: 'Ticks' },
{ translation: 'Tuberculosis' } ]
In the in IncidentController.js:
const Watson = require('../../src/services/ibmWatson');
const db_connection = require('../database/connection');
module.exports = {
async index(request, response) {
const incidents = await db_connection('incidents').join('ongs', 'ongs.id', '=', 'incidents.ong_id')
.select([
'incidents.*',
'ongs.name',
'ongs.uf']
);
const titles = [];
incidents.forEach((incident) => { titles.push(incident.title) });
const translated_titles = await Watson.translate(titles, "en");
console.log(translated_titles);
return response.json(incidents);
}
}
In the above code the console.log(response.result.translations) returns undefined.
What is wrong with it?
You are returning response.result.translations to the response callback from then().
Since that callback cannot be accessed by your IncidentController, it returns undefined.
This is one way to solve this problem:
// services/ibmWatson/index.js
translate(phrase, language) {
return new Promise((resolve, reject) => {
try {
const response = await languageTranslator.translate({ /* options */ });
resolve(response); // Return the translations
} catch(error) {
reject(error); // If there's an error, return the error
}
});
}
// IncidentController.js
async index() {
// ...
const translatedTitles = await Watson.translate(titles, "en");
console.log(translatedTitles); // Should be working now
}
I hope I could help you or at least lead you in the right direction.

How to get the result of async / await function?

I would like to return an object from the the async / await function A to pass it to another function.
Currently what I get as a result is Promise{ <pending> }' or undefined.
function A:
const parseRss = data => data.forEach(rssLink => {
const getFeed = async () => {
try {
const feed = await rssParser.parseURL(rssLink.rss);
const emailContent = {
emailBody: {
title: feed.title,
content: []
}
}
feed.items.forEach(item => {
feedObj.emailBody.content.push(`${item.title} : ${item.link}`)
});
return emailContent;
} catch (e) {
console.error(e);
}
};
return (async () => {
return await getFeed();
})();
});
Function B:
try {
const data = await getDataWithRss();
const emailData = await parseRss([{rss:'http://reddit.com/.rss'}]); // emailData is undefined
return formatEmail(emailData);
} catch (error) {
console.log(error);
}
How do I return emailContent from function A to use it in function B?
Thanks!
Since you made getFeed as async, no need another async. You are looping through, so return an array of promises. Once the you call use Promise.all to resolve. Since there could be multiple urls to fetch.
const parseRss = (data) =>
data.map((rssLink) => {
const getFeed = async () => {
try {
const feed = await rssParser.parseURL(rssLink.rss);
const emailContent = {
emailBody: {
title: feed.title,
content: [],
},
};
feed.items.forEach((item) => {
feedObj.emailBody.content.push(`${item.title} : ${item.link}`);
});
return emailContent;
} catch (e) {
console.error(e);
}
};
return getFeed();
});
try {
const data = await getDataWithRss();
const emailData = await Promise.all(parseRss([{rss:'http://reddit.com/.rss'}])); // emailData is undefined
return formatEmail(emailData);
} catch (error) {
console.log(error);
}
await will not work inside a forEach loop. Use a for...in loop instead.
actually, getFeed() is not necessary inside inner scope, you can use async in map callback:
const parseRss = data => data.map(async rssLink => {
const feed = await rssParser.parseURL(rssLink.rss);
const emailContent = {
emailBody: {
title: feed.title,
content: []
}
};
feed.items.forEach(item => {
feedObj.emailBody.content.push(`${item.title} : ${item.link}`)
});
return emailContent;
});

Node-Fetch Mapping Error - Cannot read property 'map' of undefined"

Getting an error with the "map" part when I try and run it Cannot read property 'map' of undefined"
The customers const is declared above so not sure. Where is the undefined is coming from? Does the map need declaring?
const AWS = require('aws-sdk'),
ses = new AWS.SES(),
fetch = require('node-fetch');
exports.handler = async (event) => {
console.log(event.customer_id);
const customers = await getCustomers();
customers.map(async customer => await sendEmailToCustomer(customer));
const customersEmailsPromises = customers.map(async customer => await sendEmailToCustomer(customer));
}
async function getCustomers() {
try {
const resp = await fetch('https://3objects.netlify.com/3objects.json');
const json = await resp.json();
return json;
}
catch(e) {
throw e;
}
}
const sendEmailToCustomer = (customer) => new Promise((resolve, reject) => {
ses.sendEmail({
Destination:
{ ToAddresses: [customer.email] },
Message:
{
Body: { Text: { Data: `Your contact option is ${customer.customer_id}` } },
Subject: { Data: "Your Contact Preference" }
},
Source: "sales#example.com"
}, (error, result => {
if (error) return reject(error);
resolve(result);
console.log(result);
})
);
})
getCustomers doesn't return anything which means that customers is set to undefined.
Try this:
async function getCustomers() {
try {
const resp = await fetch('https://3objects.netlify.com/3objects.json');
const json = await resp.json();
return json;
}
catch(e) {
throw e;
}
}
You also have to return something from the function that you pass as a parameter to .map
customers.map(async customer => {
return await sendEmailToCustomer(customer);
});
or just:
customers.map(async customer => await sendEmailToCustomer(customer));
And since .map returns a new array (does not mutate the original array), you'll have to store the return value:
const customersEmailsPromises = customers.map(async customer => await sendEmailToCustomer(customer));

Categories

Resources