Best practice using promises chaining in node JS - node.js

I'm little bit confusing in promises. first, I have some ugly code like this:
async function presence(ctx) {
try {
var prsenceData = [];
var isSuccess = Boolean(false);
var ckFilePath = "./somepath/cookie.json";
if (!fs.existsSync(ckFilePath)) {
await menuLogin.login(ctx).then(login => {
isSuccess = Boolean(login[0].status);
myCk.saveCookies(login[0].cookies, ckFilePath);
if (!isSuccess) {
myCk.deleteCookies(ckFilePath);
return false;
}
});
} else {
await myCk.checkToDelete(ckFilePath).then(isDel => {
if (isDel) {
return false;
}
});
}
await presenceNow.check(fs.existsSync(ckFilePath), ctx).then(data => {
for (let id = 0; id < data[0].pesan.length; id++) {
console.log(data[0].pesan[id]);
}
for (let id = 0; id < data[0].id.length; id++) {
presenceData.push(data[0].id);
}
if (data[0].pesan.length == 0 && fs.existsSync(ckFilePath)) {
myCk.deleteCookies(ckFilePath);
}
});
} catch (e) {
console.log(e);
}
return presenceData;
}
Can anyone explain why presenceNow.check() function is not calling if my ckFilePath does not exist? but if myCkFilePath is exist, my code run so well. And maybe anyone can show me the better code for that case? thanks.

Mixing async/await and promise chains like this is something of a code smell that the author lacked an understand of async/await. It's also something of a mixed metaphor.
If you refactor it to actually use async/await you get something like this that's a lot easier to understand.
My suspicion is that your presenceNow.check() method is not being called because the function is taking returning via one of the two return paths above it:
the file exists and myCk.checkToDelete() returns true, or
the file does not exist, and the login is unsuccessful.
const fs = require('fs/promises');
async function presence(ctx) {
var presenceData = [];
var isSuccess = false;
var ckFilePath = "./somepath/cookie.json";
let ckFilePathExists = await fs.access(ckFilePath);
if (ckFilePathExists) {
const isDel = await myCk.checkToDelete(ckFilePath);
if (isDel) {
return false;
}
} else {
const login = await menuLogin.login(ctx);
const isSuccess = login[0].status
myCk.saveCookies(login[0].cookies, ckFilePath);
if (!isSuccess) {
myCk.deleteCookies(ckFilePath);
return false;
}
}
ckFilePathExists = await fs.access(ckFilePath)
const data = await presenceNow.check(ckFilePathExists, ctx);
for (let id = 0; id < data[0].pesan.length; id++) {
console.log(data[0].pesan[id]);
}
for (let id = 0; id < data[0].id.length; id++) {
presenceData.push(data[0].id);
}
if (data[0].pesan.length == 0 && await fs.access(ckFilePath) ) {
myCk.deleteCookies(ckFilePath);
}
return presenceData;
}

Related

How to speed up Fetching Google Place and Photos

I currently have the following code to fetch matching Google Places according to a received query as shown below:
async function searchGoogleBusiness(req, res) {
let { name } = req.query;
const apiKey = process.env.API_KEY;
const searchUrl = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=`;
try {
let { data } = await axios.get(`${searchUrl}${name}&key=${apiKey}`)
let { status, error_message, results } = data;
if (status === 'OK') {
let businessResults = [];
if ((results ?? []).length > 0) {
for (let business of results) {
let businessDetails = {
....
}
if ((business.photos ?? []).length > 0) {
let { width = 1200, height = 1200, photo_reference } = business.photos[0];
let photoUrl = `https://maps.googleapis.com/maps/api/place/photo?photoreference=${photo_reference}&sensor=false&maxheight=${height}&maxwidth=${width}&key=${apiKey}`
try {
let businessPhotoResponse = await axios.get(photoUrl, { responseType: 'arraybuffer' });
let imageBuffer = businessPhotoResponse.data;
let base64Image = Buffer.from(imageBuffer, 'binary').toString('base64');
businessDetails.photo = `data:${businessPhotoResponse.headers['content-type']};base64,${base64Image}`;
} catch (e) {
businessDetails.photo = business.icon;
}
} else {
businessDetails.photo = business.icon;
}
businessResults.push(businessDetails);
}
}
...//Omitted
}
...//Omitted
} catch (e) {
...//Omitted
}
}
As you can immediately notice, the function takes forever to return when the results are more than 5 and the reason is because I'm looping through each business to make another api call to fetch each photo.
I don't like this approach at all.
This idea of making another network call using photoReferences is really affecting my site speed and basically just makes my users angry.
Is there no way to automatically fetch the photo urls along just in the first request?

Async call to Authorize.net not working as expected in node.js

Im setting an Authorize.com charge as per their API reference at https://developer.authorize.net/api/reference/index.html in Node.js.
I can place both ACH and CC transactions just fine, but Im facing a problem with the response from Authorize. Their response takes about 1 second.
After all the parameters such as CC number, expiration date, etc are filled out, I execute a function as follows (ctrl.execute(function () {}):
var ctrl = new ApiControllers.CreateTransactionController(createRequest.getJSON());
ctrl.execute(function () {
var apiResponse = ctrl.getResponse();
var response = new ApiContracts.CreateTransactionResponse(apiResponse);
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
//transaction approved
AuthorizeResult.status = 1;
}
else {
//transaction rejected
if (response.getTransactionResponse().getErrors() != null) {
AuthorizeResult.status = 0;
}
}
}
else {
if (response.getTransactionResponse() != null && response.getTransactionResponse().getErrors() != null) {
AuthorizeResult.status = 0;
}
else {
AuthorizeResult.status = 0;
}
}
}
else {
AuthorizeResult.status = 0;
}
After I get a result from Authorize, I need to run this code, which Im unable to do, and if I place the code inside the function, I get an error:
SyntaxError: await is only valid in async function
at wrapSafe (internal/modules/cjs/loader.js:988:16)
at Module._compile (internal/modules/cjs/loader.js:1036:27)
sqlString = `
insert into AuthorizeBillings (memberid, datetime, authorizeid, messagecode, status, amount, billingtype, errorcode)
values (#memberid, #datetime, #authorizeid, #messagecode, #status, #amount, #billingtype, #errorcode )`
try {
const pool = await utils.poolPromise
const recordset = await pool.request()
.input('memberid', utils.sql.Int, memberid)
.....
.....
.input('errorcode', utils.sql.NVarChar, AuthorizeResult.errorcode)
.query(sqlString)
} catch (err) {
console.log(err)
}
I tried to run this function await but I had no luck. What I need is to continue the code execution AFTER this function returned a value, which I am not able to accomplish properly.
Thanks.
This probably isn't a proper answer... But maybe it will help you out. In order to use await, you have to declare the function as async. So, something like this will allow async workflow:
async function AuthorizeTrans() { // Notice async declaration
var ctrl = new ApiControllers.CreateTransactionController(createRequest.getJSON());
var AuthorizeResult = {
memberid: memberid,
.....
.....
errorcode: ""
}
const res = await ctrl.execute(); // You can now use await
var apiResponse = await res.getResponse();
....... // Rest of code...
SqlExecute(params);
}
async function SqlExecute(params) {
sqlString = `
insert into AuthorizeBillings (memberid, datetime, authorizeid, messagecode, status, amount, billingtype, errorcode)
values (#memberid, #datetime, #authorizeid, #messagecode, #status, #amount, #billingtype, #errorcode )`
try {
const pool = await utils.poolPromise
const recordset = await pool.request()
.input('memberid', utils.sql.Int, memberid)
.....
.....
.input('errorcode', utils.sql.NVarChar, AuthorizeResult.errorcode)
.query(sqlString)
} catch (err) {
console.log(err)
}
}
If you follow the logic from that syntax, you should be on the right track.

Fetch API Doesn't send data on first call - NestJs

I have an API in NestJs which is not sending data on the first hit. However, on hitting it again it sends the desired data. I am guessing the API returns before the internal processing is done.
How to stop this. Is sleep a good option for this?
Or is there any other way to do this?
#Post("load")
#UseGuards(AuthGuard("jwt"))
async load(#Req() body: any)
{
const organizationId = body.user.organizationId;
const userId = body.user.userId;
if ("brandIds" in body.body)
{
await this.userService.onBoardUser(userId);
}
var settings = await this.settingsService.fetchLayout(organizationId, "home");
settings.forEach(async (element) =>
{
var parsedElement = JSON.parse(JSON.stringify(element));
var innerContent = await this.fetchContent(parsedElement.method, organizationId, userId);
var template = parsedElement.content[0];
let formattedItem = {};
innerContent.forEach((item) =>
{
try
{
formattedItem = template;
Object.keys(template).forEach((key) =>
{
if (template[key]!= "" && key != "type")
{
formattedItem[key] = eval(template[key]);
}
});
parsedElement.content.push(formattedItem);
formattedItem = null;
}
catch(err)
{
}
});
this.response.data.push(parsedElement);
innerContent = null;
template = null;
formattedItem = null;
parsedElement = null;
});
return(this.response);
}
looks like your main problem here is that your using async/await inside foreach which isnt working.
Use it like this:
for (const setting of settings) {
... your async code here.
}

How to handle a long request with Express

I'm working on a simple function I have for a specific GET request triggered in the browser. The objective of this request is to make multiple queries to a mongodb (mongoose) database and then perform some calculation and structure formating on the results to send it back to the browser.
The only problem is that everything takes too long and it results in an error in the browser:
net::ERR_EMPTY_RESPONSE
to give an example of part of the function I'm trying to build here it goes:
async function getPriceByMake(makes, id) {
return new Promise(async (resolve, reject) => {
let pMakes = {};
const makesArr = Object.keys(makes);
for (let i = 0; i < makesArr.length; i++) {
console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
const currMake = makesArr[i];
pMakes[currMake] = {};
const modelsArr = Object.keys(makes[currMake]);
for (let j = 0; j < modelsArr.length; j++) {
const currModel = modelsArr[j];
await Listing.find({ catFrom: id, model: currModel }, 'year asking', (err, docs) => {
if (docs.length > 1) {
pMakes[currMake][currModel] = [docs];
} else {
pMakes[currMake][currModel] = {};
}
});
}
}
resolve(pMakes);
});
}
In this function, if I leave the async / await out, I get an empty {} on the other end. Which is obviously not the objective.
I've been searching the web a little and was able to find an article pointing to this scheme:
Browser:
Initiates request
displays progress
Show result
WebServer:
Submit event
Checks for completion
Return result
BackEndApp:
Picks up event
Runs task
Returns results
My question is the following:
How can I do that with NodeJS and Express?
In this code:
for (let j = 0; j < modelsArr.length; j++) {
const currModel = modelsArr[j];
await Listing.find({ catFrom: id, model: currModel }, 'year asking', (err, docs) => {
if (docs.length > 1) {
pMakes[currMake][currModel] = [docs];
} else {
pMakes[currMake][currModel] = {};
}
});
}
Your await isn't working because you're passing a callback to Listing.find(). When you do that, it does NOT return a promise and therefore the await does nothing useful. You get the empty response because the await doesn't work and thus you call resolve() before there's any actual data there.
Change the code to this:
for (let j = 0; j < modelsArr.length; j++) {
const currModel = modelsArr[j];
let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
if (docs.length > 1) {
pMakes[currMake][currModel] = [docs];
} else {
pMakes[currMake][currModel] = {};
}
}
And, then the await will work properly.
You also should remove the return new Promise() wrapper. You don't want that. Just make the function async and use await and it will already return a promise.
Here's your function with the unnecessary promise wrapper removed:
async function getPriceByMake(makes, id) {
let pMakes = {};
const makesArr = Object.keys(makes);
for (let i = 0; i < makesArr.length; i++) {
console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
const currMake = makesArr[i];
pMakes[currMake] = {};
const modelsArr = Object.keys(makes[currMake]);
for (let j = 0; j < modelsArr.length; j++) {
const currModel = modelsArr[j];
let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
if (docs.length > 1) {
pMakes[currMake][currModel] = [docs];
} else {
pMakes[currMake][currModel] = {};
}
}
}
return pMakes;
}
Then, keep in mind that whatever code sends your actual response needs to use .then() or await when calling this async function in order to get the final result.
Your best bet to speed up this code would be to refactor either your queries or your database structure or both to not have to do N * M separate queries to get your final result. That's likely where your slowness is coming from. The biggest performance gains will probably come from reducing the number of queries you have to run here to far fewer.
Depending upon your database configuration and capabilities, it might speed things up to run the inner loop queries in parallel as shown here:
async function getPriceByMake(makes, id) {
let pMakes = {};
const makesArr = Object.keys(makes);
for (let i = 0; i < makesArr.length; i++) {
console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
const currMake = makesArr[i];
pMakes[currMake] = {};
const modelsArr = Object.keys(makes[currMake]);
await Promise.all(modelsArr.map(async currModel => {
let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
if (docs.length > 1) {
pMakes[currMake][currModel] = [docs];
} else {
pMakes[currMake][currModel] = {};
}
}));
}
return pMakes;
}

nodejs retry function if failed X times

I want my function to execute X(=3) times until success.
In my situation I'm running kinesis.putRecord (from AWS API), and if it fails - I want to run it again until it succeeds, but not more than 3 tries.
I'm new to NodeJS, and the code I wrote smells bad.
const putRecordsPromise = function(params){
return new Promise((resolve, reject) => {
kinesis.putRecord(params, function (err, data) {
resolve(err)
});
})
}
async function waterfall(params){
try{
let triesCounter = 0;
while(triesCounter < 2){
console.log(`try #${triesCounter}`)
let recordsAnswer = await putRecordsPromise(params)
if(!recordsAnswer){
console.log("success")
break;
}
triesCounter += 1;
}
// continue ...
} catch(err){
console.error(err)
}
}
waterfall(params)
I promise the err result. Afterwards, If the err is empty, then all good. otherwise, continue running the same command.
I'm sure there is a smarter way to do this. Any help would be appreciated.
I think, all the Aws functions can return a Promise out of the box, then you can just put the call into try/catch:
let triesCounter = 0;
while(triesCounter < 2){
console.log(`try #${triesCounter}`)
try {
await kinesis.putRecord(params).promise();
break; // 'return' would work here as well
} catch (err) {
console.log(err);
}
triesCounter ++;
}
In functional style:
...
await tryUntilSucceed(() => kinesis.putRecord(params).promise());
...
async function tryUntilSucceed(promiseFn, maxTries=3) {
try {
return await promiseFn();
} catch (e) {
if (maxTries > 0) {
return tryUntilSucceed(promiseFn, maxTries - 1);
}
throw e;
}
}
Make a little module, say try-and-try-again.js:
exports = module.exports = tryAndTryAgain;
function tryAndTryAgain( maxTries, thisContext , fn, ...argv) {
let success = false;
for (let i = i ; i < maxTries && !success ; ++i ) {
let rc = fn.apply(thisContext, args);
success = rc == 0 ? true : false;
}
return success;
}
Then you can use it anywhere:
const tryAndTryAgain = require('./try-and-try-again');
function somethingThatMightNeedARetry() { ... }
const succeeded = tryAndTryAgain( 3 , null, somethingThatMightNeedARetry, 'arg-1', 'arg-2', 'arg-3' );
There is an npm package called async-retry that is pretty handy. It acts as a wrapper for your function and retries if anything throws (with some exceptions that you can handle, see their example below).
// Packages
const retry = require('async-retry')
const fetch = require('node-fetch')
await retry(async bail => {
// if anything throws, we retry
const res = await fetch('https://google.com')
if (403 === res.status) {
// don't retry upon 403
bail(new Error('Unauthorized'))
return
}
const data = await res.text()
return data.substr(0, 500)
}, {
retries: 5
})

Resources