This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I'm trying to get the value returned from an API query, but in all the ways I've done it, it either returns Undefined or [object Promise]. I've tried several solutions and I still haven't got something that works. Below is the last code I made to see if it worked but again without success.
function generateLink(link) {
const url = 'https://api.rebrandly.com/v1/links';
const options = {
method: 'POST',
uri: url,
headers: {'Content-Type': 'application/json', apikey: 'xxxxxxxxxxxxxxxxxxxxx'},
body: JSON.stringify({destination: link})
};
return requestPromise(options).then(response => {
if ( response.statusCode === 200 ) {
return response.body
}
return Promise.reject(response.statusCode)
})
}
...
bot.onText(/\/offers (.+)/, function onEchoText(msg, match) {
console.log(match[1]);
if (isNaN(match[1])) {
bot.sendMessage(msg.from.id, 'Enter a valid number! \nExample: /offers 5');
} else {
client.execute('aliexpress.affiliate.product.query', {
'app_signature': 'defualt',
'category_ids': '701,702,200001081,200001388,200001385,200386159,100000616,100001205,5090301',
'target_currency': 'USD',
'target_language': 'EN',
'tracking_id': 'defualt',
'ship_to_country': 'US',
}, function (error, response) {
var code = response.resp_result.resp_code;
var mesage = response.resp_result.resp_msg;
if (code === 200) {
var i;
var temCupom = [];
var link = [];
var itemList = response.resp_result.result.products.product;
for (i = 0; i < match[1]; i++) {
temCupom[i] = itemList[i].promo_code_info ? "🏷 <b>There's a coupon!</b>: " + itemList[i].promo_code_info.promo_code : "";
(async () => {
link[i] = generateLink(itemList[i].promotion_link).then(body => { return body.shortUrl })
})();
bot.sendPhoto(msg.chat.id, itemList[i].product_main_image_url, {
caption: "❤ <b>Promotion</b> ❤\n" +
"💵 <b>Price</b>: " + Number(itemList[i].target_sale_price).toLocaleString('en-us', { style: 'currency', currency: 'USD' }) + "\n" +
"🛒 <b>Link</b>: " + link[i] + "\n" +
temCupom[i],
});
}
}
else {
bot.sendMessage(msg.from.id, 'Deu errado! ' + mesage);
}
})
}
});
link[i] need to return the links generated with the API
Option 1 (with async/await):
if (code === 200) {
var i;
var link = [];
var itemList = response.resp_result.result.products.product;
for (i = 0; i < match[1]; i++) {
link[i] = await generateLink(itemList[i].promotion_link).then(body => { return JSON.parse(body).shortUrl })
}
}
Option 2 (with Promise):
if (code === 200) {
var link = [];
var itemList = response.resp_result.result.products.product;
for (let i = 0; i < match[1]; i++) {
generateLink(itemList[i].promotion_link).then(body => { link[i] = JSON.parse(body).shortUrl })
}
}
Option 3 (Promise.all takes all URLs in parallel mode):
if (code === 200) {
const itemList = response.resp_result.result.products.product;
const requests = itemList.slice(0, match[1])
.map(item => generateLink(item.promotion_link).then(body => JSON.parse(body).shortUrl));
const links = await Promise.all(requests);
}
===============================
Updated:
After all, we realized that the body was a string and it was necessary to do JSON.parse
You need to assign the result in the then block.
You can make some simple changes, like putting link[i] = result into the then block, but there are problems like the fact that value of i will be the same. Also, if you want the values populated after the for loop, you'll notice that they have not been filled. You need to wait till they are all resolved (faking with timeout below).
function generateLink(link) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Math.random())
}, 1)
})
}
if (200 === 200) {
var i;
var link = [];
var MAX = 10;
for (i = 0; i < MAX; i++) {
generateLink('🤷♂️').then(result => {
link[i] = result; // no bueno - since i will be 10 when this is resolved
})
}
console.log(link) // still not resolved
setTimeout(() => {
console.log(link) // 🤦♂️ [undefined * 9, value]
}, 10)
}
// async version (note use of `async`)
(async function(){
var MAX = 10;
var link = []
for (i = 0; i < MAX; i++) {
link[i] = await generateLink('🤷♂️')
}
console.log(link)// 👍
})()
So you can add more complexity to make it work, but async/await is likely the way to go if you can support it.
for (i = 0; i < match[1]; i++) {
link[i] = await generateLink(itemList[i].promotion_link).then((body) => {
return body.shortUrl;
});
}
Also if you use await the promises will execute in series, which you might not want. Resolving in parallel is faster, but you could also run into issue if you're going to have too many network requests they'll need to be throttled, so async await may work better)
My use case demands me to call an sql recursively till no rows are returned for which I have written the below code which due to async nature doesn't work as expected.
The piece of code which does this invocation is:
let Response = await getData(userId);
async function getData(userId) {
console.log("Invoking Get Data Function");
let arrayOfUserId = [userId];
let fetchMore = true,
j = 1;
let keyWithQoutes = -1;
return new Promise((resolve, reject) => {
do {
console.log(arrayOfUserId, j)
j++;
if (arrayOfUserId.length > 0) {
keyWithQoutes = arrayOfUserId.map((it) => {
return `'${it}'`;
});
}
const sql = ` Select userId from USER where reportingTo in (${arrayOfUserId})`;
console.log(' SQL Query ', sql);
con.query(sql, [], async(error, response) => {
if (error) {
fetchMore = false;
reject(error);
}
console.log(
" Response for ",
userId,
response,
response.length
);
if (response.length == 0) {
fetchMore = false;
resolve(arrayOfUserId);
}
else {
for (let i = 0; i < response.length; i++) {
console.log(response[i].userId);
arrayOfUserId.push(response[i].userId);
}
}
});
} while (fetchMore);
});
}
I am trying to register a user using AWS Cognito. In the below class, the control from theregisterInCognito is not being returned. I have tried adding return after the failure and success conditions too (aren't reject and resolve of the promise the same?). But nothing worked. When I tried debugging, I figured out that the section where I have commented <---- The control is unreachable here. is not being executed. The control itself is in the return function.
export class CompanyCAO {
// change return type from any to meaningful type
private poolData;
private pool_region;
private userPool;
private cognitoUser;
constructor() {
this.poolData = {
UserPoolId: process.env.COGNITO_USER_POOL_ID,
ClientId: process.env.COGNITO_CLIENT_ID
}
this.pool_region = process.env.COGNITO_POOL_REGION;
this.userPool = new AmazonCognitoIdentity.CognitoUserPool(this.poolData);
}
public create(regInfo: Company): Promise < any > {
return new Promise < boolean > (async(resolve, reject) => {
this.registerInCognito(regInfo);
});
}
public registerInCognito(regInfo: Company): Promise < boolean > {
return new Promise < boolean > (async(resolve, reject) => {
let attributes = process.env.COGNITO_AUTH_ATTRIBUTES!.split(",");
let attributeValues = Array < string > ();
let attributesList = Array < any > ();
for (var attribute in attributes) {
for (var attributeName in regInfo) {
if (((typeof regInfo[attributeName]) != 'object') && (attributes[attribute] == attributeName)) {
attributeValues.push(regInfo[attributeName]);
} else if ((typeof regInfo[attributeName]) === 'object') {
for (var subAttributeName in regInfo[attributeName]) {
if (subAttributeName == attributes[attribute]) {
attributeValues.push(regInfo[attributeName][subAttributeName]);
}
}
}
}
}
for (var index in attributes) {
attributesList.push(new AmazonCognitoIdentity.CognitoUserAttribute({
Name: attributes[index],
Value: attributeValues[index]
}));
}
const randomPassword = this.createRandomPassword();
console.log(`Password is ${randomPassword}`);
await this.userPool.signUp(regInfo.Admin.EmailAddress, randomPassword, attributesList, null, (err, result) => {
if (err) {
console.error(err);
reject(err);
} else {
this.cognitoUser = result.user;
}
}).promise().then((value: any) => {
resolve(true);
}).catch(error => {
reject(error);
})
});
// <---- The control is unreachable here.
}
public createRandomPassword() {
let password = '';
const alphaCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numberCharacters = '1234567890';
const specialCharacters = '!##$%^&**()?/><}{';
const iterations = 5;
for (let i = 0; i < iterations; i++) {
password += alphaCharacters.charAt(Math.floor(Math.random() * alphaCharacters.length));
password += numberCharacters.charAt(Math.floor(Math.random() * numberCharacters.length));
password += specialCharacters.charAt(Math.floor(Math.random() * specialCharacters.length));
}
return password;
}
}
I'm trying to access objects properties inside an array in my code to render the text values of input boxes restored after a refresh from the local storage but for some reason when I try to run the for loop inside my appStart() function it gives me: "Uncaught TypeError: Cannot read property 'id' of undefined at appStart". Any insiights of why this happens and how to fix it will be greatly appreciated.
const currentDayPlaceholder = $("#currentDay");
const timeInTimeBlocks = $(".input-group-text");
const timeBlockInput = $(".form-control");
const saveButton = $(".saveBtn");
let numericCurrentTime = parseInt(moment().format("H A"));
let notes = [];
currentDayPlaceholder.append(moment().format('dddd, MMMM Do'));
function timeBlocksColorDeterminator() {
for (let i = 0; i < timeInTimeBlocks.length; i++) {
let numericTimeinTimeBlock = parseInt($(timeInTimeBlocks[i]).text());
if ($(timeInTimeBlocks[i]).hasClass('pm')) {
numericTimeinTimeBlock += 12;
}
if (numericCurrentTime === numericTimeinTimeBlock) {
$(timeBlockInput[i]).addClass("present");
} else if (numericCurrentTime > numericTimeinTimeBlock) {
$(timeBlockInput[i]).addClass("past");
} else {
$(timeBlockInput[i]).addClass("future");
}
}
}
function appStart() {
notes = JSON.parse(localStorage.getItem("timeBlockNotes"));
for (let i = 0; i < timeBlockInput.length; i++) {
if (i === parseInt(notes[i].id)) {
timeBlockInput[i].value = notes[i].value;
}
}
}
appStart();
saveButton.on("click", function () {
console.log("click");
notes.push({
value: timeBlockInput[this.id].value,
id: this.id
})
localStorage.setItem("timeBlockNotes", JSON.stringify(notes));
})
timeBlocksColorDeterminator();
I have fixed this after changing my appStart() function to this :
function appStart() {
notes = JSON.parse(localStorage.getItem("timeBlockNotes"));
for (let i = 0; i < notes.length; i++) {
timeBlockInput[parseInt(notes[i].id)].value = notes[i].value;
}
}
thank you guys for your comments and answers.
Hi I have a for loop in my node js application which calls an async function. I want to check a value and decide whether a customer is found or not. But the loop iterates until the last element. Hence my error loop is not working. I want the loop to check the response and then iterate the next loop.
for loop:
for (let i = 0; i < customerlookupresponse.data.length; i++) {
var customer = customerlookupresponse.data[i];
if (customer != undefined) {
console.log("customer.id :: " + customer.id)
var accountlookUpData = {
customerId: customer.id
};
customerAccountLookUpRequest(accountlookUpData).then(data => {
console.log("----" + i + " -- " + data);
if (data && data.status === 1) {
resolve(data);
return;
}else{
reject({
status: 404,
message: "Customer not found"
});
return;
}
});
} else {
reject({
status: 404,
message: "Customer not found"
});
return;
}
}
the async function:
async function customerAccountLookUpRequest(customerLookUpData) {
var accountLookUp = config.app.url;
let data = await axios.get(accountLookUp).then(accountLookUpResult => {
for (i = 0; i < accountLookUpResult.data.length; i++) {
var requestaccount = accountLookUpResult.data[i].accountNumber;
if (requestaccount == customerLookUpData.lookupAccount) {
accountLookUpResult.data[i].customerId = customerLookUpData.customerId;
accountLookUpResult.data[i].status = 1;
return accountLookUpResult.data[i];
}
}
});
return data;
}
I am new to node js and trying to understand the concept of async await. Please help.
An async function waits for a Promise to return. The function that has the loop should be declared as async and the customerAccountLookUpRequest function should return a promise. Then use the await operator to call the function. Simple example:
class some_class {
constructor() {
}
async my_loop() {
let _self = this;
for (let i = 0; i < customerlookupresponse.data.length; i++) {
let data = await _self.customerAccountLookUpRequest(accountlookUpData);
console.log("----" + i + " -- " + data);
}
}
customerAccountLookUpRequest(customerLookUpData) {
return new Promise((resolve, reject) => {
axios.get(accountLookUp).then(accountLookUpResult => {
resolve(accountLookUpResult);
});
});
}
}