I have a code to fetch directory names from first API. For every directory, need to get the file name from a second API. I am using something like this in my Node JS code -
async function main_function(req, res) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
console.log(tempPromises); // Prints [ Promise { <pending> } ]
Promise.all(tempPromises).then((result_new) => {
console.log(result_new); // This prints "undefined"
res.send({ status: "ok" });
});
});
}
async function getFilename(inp_a) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
return new Promise((resolve) => {
resolve("Temp Name");
});
});
}
What am I missing here?
Your getFilename() doesn't seem to be returning anything i.e it's returning undefined. Try returning response at the end of the function,
async function getFilename(inp_a) {
const response = ...
return response;
}
Thanks to Mat J for the comment. I was able to simplify my code and also learn when no to use chaining.
Also thanks to Shadab's answer which helped me know that async function always returns a promise and it was that default promise being returned and not the actual string. Wasn't aware of that. (I am pretty new to JS)
Here's my final code/logic which works -
async function main_function(req,res){
try{
const response = await fetch(...)
const resp = await response.text();
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
Promise.all(tempPromises).then((result_new) => {
console.log(result_new);
res.send({ status: "ok" });
});
}
catch(err){
console.log(err)
res.send({"status" : "error"})
}
}
async function getFilename(inp_a) {
const response = await fetch(...)
respText = await response.text();
return("Temp Name"); //
}
I'm trying to send push notifications to 10 000+ tokens stored in Firestore.
Each time, I got a DEADLINE_EXCEEDED error.
I have already updated the timeout to 300s.
I don't know how to improve my code to avoid this error.
const pagination = 200;
function parseAndSend(push) {
const parse = async(request, total) => {
let snapshot = await request.get();
let lastVisible = snapshot.docs[snapshot.docs.length-1];
let length = snapshot.size;
var users= {};
snapshot.forEach((doc) => {
pushHelperFunctions.addUsers(doc, users);
});
pushHelperFunctions.sendMessage(users, push);
if(length < pagination) {
return console.log("finish", total+length);
} else {
let next = admin.firestore().collection("users").startAfter(lastVisible).limit(pagination);
return parse(next, total+length);
}
}
return parse(admin.firestore().collection("users").limit(pagination), 0);
}
async function sendMessage(users, push) {
Object.keys(users).forEach(key => {
if (users[key].length > 0) {
let message = payloads.setMessage(users[key], key, push);
// Send notifications to all tokens.
admin.messaging().sendMulticast(message).then(response => {
console.log(response.successCount + " messages were sent successfully");
cleanupTokens(response, users[key]);
return true;
}).catch(error => {
mailer.sendMail("messaging().sendToDevice - Error sending message "+push);
console.log("Error sending message:", error);
return false;
});
}
});
}
// Cleans up the tokens that are no longer valid.
function cleanupTokens(response, tokens) {
// For each notification we check if there was an error.
if (response.failureCount > 0) {
const tokensDelete = [];
const failedTokens = [];
response.responses.forEach((resp, index) => {
if (!resp.success) {
failedTokens.push(tokens[index]);
const deleteTask = admin.firestore().collection('users').doc(tokens[index]).delete();
tokensDelete.push(deleteTask);
}
});
console.log('List of tokens that caused failures: ' + failedTokens);
return Promise.all(tokensDelete);
} else {
return null;
}
}
Also I don't find a way to test my code without sending a prod message to my users... It makes testing difficult.
I have a NodeJS+Express REST API method executing reverse geocoding (using Google's Maps API).
I'm trying to solve it with Promises but the 'then' is getting executed before my function returns with the answers from Google.
When testing the same code just calling a setTimeout, it works as expected. Please see comments in the code (simplify version).
app.get('/api/v1/events', verifyToken, async (req, res) => {
await db.poolPromise.then(pool => {
return pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
}).then(result => {
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
}
else if (result.recordset.length > 0) {
(new Promise( (resolve) => {
//resolve(123) // this one works as expected
//setTimeout(resolve, 3000, 'temp success') // this one works as expected
// *** this one get passed and the following then is being executed before it answers ***
resolve( getAddress_TEST(result.recordset) )
// **************************************************************************************
})).then(function (value) {
res.json(
{
meta: { count: 10 }, //this is just a sample
result: value // *** this one fails with undefined ***
})
})
} else {
res.sendStatus(404)
}
}).catch(err => {
res.sendStatus(500)
console.error(err)
})
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
//sample recordset for debugging - as you dont have my database
recordset = [{'eventId':14205556,'Lat':54.57767,'Lon':-2.4920483},{'eventId':14205558,'Lat':54.57767,'Lon':-2.492048},{'eventId':14205579,'Lat':53.416908,'Lon':-2.952071},{'eventId':14205588,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205601,'Lat':52.29174,'Lon':-1.532283},{'eventId':14205645,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205801,'Lat':53.68687,'Lon':-1.498708},{'eventId':14206041,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206049,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206072,'Lat':51.471521,'Lon':-0.2038033}]
let geocoder = nodeGeocoder(nodeGeocoder_options)
let ps = []
for (var i = 0, length = recordset.length; i < length; i++) {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == recordset[i].Lat
&& recordset[i - 1].Lon == recordset[i].Lon)) {
ps.push(new Promise(function (resolve) {
resolve(reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
}))
} else {
ps.push('-')
}
}
await Promise.all(ps)
.then(function (values) {
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
}).then(function () {
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
})
};
async function reverseGeocode(geocoder, lat, lon) {
let address = '+'
if (lat != 0 && lon != 0) {
await geocoder.reverse({ lat: lat, lon: lon })
.then(res => {
address = res[0].formattedAddress
})
.catch(err => {
console.error(err)
});
}
return address
};
I'm sure it is something simple that I'm missing here...
The basic problem is that your getAddress_TEST function returns a promise that fulfills with nothing (undefined), because it does not contain a return statement. The return recordset is in a then() callback, from where it affects the promise resolution of the awaited promise, but that result is thrown away.
If you want to use async/await, you should get rid of any new Promise and then calls:
app.get('/api/v1/events', verifyToken, async (req, res) => {
try {
const pool = await db.poolPromise
const result = await pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
} else if (result.recordset.length > 0) {
const value = await getAddress_TEST(result.recordset)
res.json({
meta: { count: 10 }, //this is just a sample
result: value
})
} else {
res.sendStatus(404)
}
} catch(err) {
res.sendStatus(500)
console.error(err)
}
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
const geocoder = nodeGeocoder(nodeGeocoder_options)
const ps = recordset.map((record, i) => {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == record.Lat
&& recordset[i - 1].Lon == recordLon)) {
return reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
} else {
return '-'
}
});
const values = await Promise.all(ps)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
// ^^^^^^^^^^^^^^^^
}
async function reverseGeocode(geocoder, lat, lon) {
if (lat != 0 && lon != 0) {
const res = await geocoder.reverse({ lat: lat, lon: lon })
return res[0].formattedAddress
}
return '+'
}
I am using puppeteers. I have created a page.on('response') that is listening that is listening for requests.
I have a loop that takes care of scrolling. How can I detect if scrolling raises the 'response' event?
I was thinking of returning a boolean from the event, but how could I capture it?
page.on('response', (response) => {
if (response.url().indexOf('page') >= 0) {
return true;
} else {
return false;
}
});
while(items.length < howMuchItems) {
await page.evaluate((sel) => {
window.scrollBy(0, document.scrollingElement.querySelector(sel).scrollHeight);
}, selectors.CONTAINER_SCROLLED);
// Detect if exists event response
// If doesn´t exist => break loop
items= await page.$$(selectors.ITEM);
}
I dont know your program logic, but generally you must create like this code
const waitForNewItems = () =>
new Promise((resolve, reject) =>
page.once('response', (response) => {
if (response.url().indexOf('page') >= 0) {
return resolve(true);
} else {
return resolve(false);
}
});
while(items.length < howMuchItems) {
await page.evaluate((sel) => {
window.scrollBy(0, document.scrollingElement.querySelector(sel).scrollHeight);
}, selectors.CONTAINER_SCROLLED);
// Detect if exists event response
// If doesn´t exist => break loop
const bool = await waitForNewItems();
if (!bool) break;
items= await page.$$(selectors.ITEM);
}
var geoCorrectionJob = scheduler.scheduleJob('*/30 * * * * *', function(){
//Code section 1
mysqlService.getJoinedMySQLDataWithSuccess().then(function(result) {
// console.log(result) //will log results.
var response, attractionId;
for(var pos = 0; pos < result.length; pos++){
// attractionid = result[pos].attractionid
geolocationController.functionUsingPromise(result[pos]).then(function(response) { // `delay` returns a promise
// console.log(response); // Log the value once it is resolved
attractionId = response[0];
if(response[1] == 1){
attractionsDone.push(attractionId);
if(attractionsDone.length == config.scheduler.UPDATE_SIZE || pos == result.length - 1){
mysqlService.MySQL(attractionsDone).then(function(temp){
attractionsDone = [];
})
.catch((err) => {
console.log(err);
});;
}
}
else if(response[1] == 0){
mysqlService.MySql(attractionId);
}
})
.catch((err) => {
console.log(err);
});
}
})
.catch((err) => {
console.log(err);
});
// Code section 2
mysqlService.getProcessStatusOfAttractions().then(function(status){
// console.log(status);
var processed = status[0].STATUS;
var unprocessed = status[1].STATUS;
var noData = status[2].STATUS;
emailService.sendEmail(processed, noData, unprocessed);
})
.catch((err) => {
console.log(err);
});
});
How do I ensure that "Code section 2" is executed after "Code section 1" is completely executed? Code section 1 has a Promisified task within a for loop. I want Code section 2 to be executed after all the tasks in Code section 1 are completed.
var geoCorrectionJob = scheduler.scheduleJob('*/30 * * * * *', function(){
// console.log("Test Job");
var promiseArr = [];
mysqlService.getJoinedMySQLDataWithSuccess().then(function(result) {
// console.log(result) //will log results.
var response, attractionId;
for(var pos = 0; pos < result.length; pos++){
// attractionid = result[pos].attractionid
promiseArr.push(new Promise((resolve, reject) => {
geolocationController.getLatLngWithPromise(result[pos]).then(function(response) { // `delay` returns a promise
// console.log(response); // Log the value once it is resolved
attractionId = response[0];
if(response[1] == 1){
attractionsDone.push(attractionId);
if(attractionsDone.length == config.scheduler.UPDATE_SIZE || pos == result.length - 1){
mysqlService.markAttractionsDoneInMySQL(attractionsDone).then(function(temp){
attractionsDone = [];
resolve();
})
.catch((err) => {
console.log(err);
reject();
});;
}
else{
resolve();
}
}
else if(response[1] == 0){
mysqlService.markAttractionNotHavingDataInMySQL(attractionId);
resolve();
}
})
.catch((err) => {
console.log(err);
reject();
});
}));
}
Promise.all(promiseArr).then(() => {
mysqlService.getProcessStatusOfAttractions().then(function(status){
console.log(status);
var processed = status[0].STATUS;
var unprocessed = status[1].STATUS;
var noData = status[2].STATUS;
emailService.sendEmail(processed, noData, unprocessed);
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
console.log(promiseArr); // empty array
});