nodejs retry function if failed X times - node.js

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

Related

async function wont break node.js (.mjs)

I'm trying to pause this function using the breakTheLoop var but it wont work.
This is the code if anyone could help me out
`
async afk(message) {
var breakTheLoop = false;
while (!breakTheLoop) {
await new Promise(resolve => setTimeout(resolve, 500));
this.bot.setControlState('forward', true);
await this.bot.waitForTicks(1);
//this.bot.setControlState('sprint', true);
this.bot.setControlState('jump', true);
let arm = Math.random() < 0.5 ? 'right' : 'left';
await this.bot.swingArm(arm);
let yaw = 2*Math.random()*Math.PI - (0.5*Math.PI);
let pitch = Math.random()*Math.PI - (0.5*Math.PI);
await this.bot.look(yaw,pitch,false);
await this.bot.waitForTicks(11);
if (message === '!afkoff') {
return breakTheLoop = true;
}
}}
This is the function i use to call to the async function
chatLog(username, message) {
if (!botNames.includes(username)) {
this.log(chalk.ansi256(98)(`<${username}>`), message)
if(message === '!afk')this.afk(), this.bot.chat("I'm AFK!");
}
}
I'm new to js so any help would be appreciated.
Expected the loop to stop but it just keeps going i've tried multiple answers on here.

Best practice using promises chaining in 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;
}

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.

nodejs async/await isn't waiting

I have a code block that where there are two await statements. The very first time the server is running I believe it is blowing right by the await SQL.connect. After there's been a couple of calls (allowing for time for connection) through this then the procedure works fine.
This is for NodeJS, MSSQL package.
Why is my first await, not waiting?
async execStoredProc(spDescriptor: StoredProcedureDescriptor) {
if (!this.pool) {
if (this.isFirst) {
this.isFirst = false ;
} else {
SQL.close();
}
this.pool = await SQL.connect(this.config);
}
try {
const request = new SQL.Request(this.pool) ;
spDescriptor.parms.forEach(function(sqlParm: SqlParm){
request.input(sqlParm.name, sqlParm.sqlType, sqlParm.value) ;
})
const qryresult = await request.execute(spDescriptor.storedProcedureName)
let result = {} ;
if (spDescriptor.multipleResultSets) {
result = qryresult.recordsets ;
} else {
result = qryresult.recordset ;
}
return result ;
} catch (err) {
console.log("SQL Error===>", err);
return [] ;
}
}

Proper way to make callbacks async by wrapping them using `co`?

It is 2016, Node has had nearly full ES6 support since v4, and Promises have been around since 0.12. It's time to leave callbacks in the dust IMO.
I'm working on a commander.js-based CLI util which leverages a lot of async operations - http requests and user input. I want to wrap the Commander actions in async functions so that they can be treated as promises, and also to support generators (useful for the co-prompt library I'm using for user input).
I've tried wrapping the CB with co in two ways:
1)
program.command('myCmd')
.action(program => co(function* (program) {...})
.catch(err => console.log(err.stack)) );
and
2) program.command('myCmd').action(co.wrap(function* (program) { .. }));
The problem with 1) is that the program parameter isn't passed
The problem with 2) is that errors are swallowed...
I'd really like to get this working as it yields much nicer code in my use case - involving a lot of http requests and also waiting for user input using the co-prompt library..
Is it a better option altogether perhaps to wrap program.Command.prototype.action somehow?
thanks!
I've used a bespoke version of something like co to get a db.exec function which uses yield to do database request. You can pass parameters into a generator function (I pass in a connection object - see the comment where I do it).
Here is by db.exec function that is very similar to what co does
exec(generator) {
var self = this;
var it;
debug('In db.exec iterator');
return new Promise((accept,reject) => {
debug('In db.exec Promise');
var myConnection;
var onResult = lastPromiseResult => {
debug('In db.exec onResult');
var obj = it.next(lastPromiseResult);
if (!obj.done) {
debug('db.exec Iterator NOT done yet');
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
debug('db.exec released connection');
}
accept(obj.value);
debug('db.exec Promise Resolved with value %d',obj.value);
}
};
self._connection().then(connection => {
debug('db.exec got a connection');
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
logger('database', 'Exec Function Error: ' + error.message);
reject(error);
});
});
}
the connection object also wraps by database connection object and provides a generator function ability to process the rows of the results from the database, but I won't post that here (although the example below is using it to process the rows).
Here is an example of using the exec function to run a sequence of sql
db.exec(function*(connection) {
if (params.name === ADMIN_USER) {
debug('Admin Logon');
user.name = ADMIN_DISPLAY;
user.keys = 'A';
user.uid = 0;
let sql = 'SELECT passwordsalt FROM Admin WHERE AdminID = 0';
connection.request(sql);
yield connection.execSql(function*() {
let row = yield;
if (row) {
user.nopass = (row[0].value === null);
} else {
user.nopass = false;
}
debug('Admin Password bypass ' + user.nopass.toString());
});
} else {
debug('Normal User Logon');
let sql = `SELECT u.UserID,PasswordSalt,DisplayName,AccessKey,l.LogID FROM Users u
LEFT JOIN UserLog l ON u.userID = l.userID AND DATEDIFF(D,l.LogDate,GETDATE()) = 0
WHERE u.UserName = #username`;
let request = connection.request(sql);
request.addParameter('username',db.TYPES.NVarChar,params.name);
let count = yield connection.execSql(function*() {
let row = yield;
if (row) {
user.uid = row[0].value;
user.name = row[2].value;
user.keys = (row[3].value === null) ? '' : row[3].value;
user.nopass = (row[1].value === null) ;
user.lid = (row[4].value === null) ? 0 : row[4].value;
debug('Found User with uid = %d and lid = %d, keys = %s',
user.uid, user.lid, user.keys);
}
});
if (count === 0) {
debug('Not Found User');
// couldn't find name in database
reply(false,false);
return;
}
}
if (!user.nopass) {
debug('Need a Password');
//user has a password so we must check it
passGood = false; //assume false as we go into this
let request = connection.request('CheckPassword');
request.addParameter('UserID',db.TYPES.Int,user.uid);
request.addParameter('password',db.TYPES.VarChar,params.password);
yield connection.callProcedure(function*() {
let row = yield;
if (row) {
//got a valid row means we have a valid password
passGood = true;
}
});
} else {
passGood = true;
}
if (!passGood) {
debug('Not a Good Pasword');
reply(false,true);
} else {
if (user.uid !== 0 && user.lid === 0) {
let sql = `INSERT INTO UserLog(UserID,LogDate,TimeOn,UserName) OUTPUT INSERTED.logID
VALUES(#uid,GETDATE(),GETDATE(),#username)`;
let request = connection.request(sql);
request.addParameter('uid',db.TYPES.Int,user.uid);
request.addParameter('username',db.TYPES.NVarChar,user.name);
yield connection.execSql(function*() {
let row = yield;
if (row) {
user.lid = row[0].value;
debug('Users Log Entry = %d',user.lid);
}
});
}
reply(true,user);
}
})
.catch((err) => {
logger('database','Error on logon: ' + err.message);
reply(false,false);
});
});
There is a quite simple way to do async function in Commander.js
async function run() {
/* code goes here */
}
program
.command('gettime')
.action(run);
program.parse(process.argv);

Resources