Node JS: Request Loop Until Status Code 200 - javascript

I currently have working code that does a request and checks if it receives a successful status code of 200. I would like to grow on this and loop it where it will keep sending requests until the status code is 200. I tried using a while loop but was not receiving the correct results. Thanks for the help!
request('http://0.0.0.0:9200', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log('success');
do(something);
}
else {
console.log('fail');
}
});

Would be something like:
let retry = (function() {
let count = 0;
return function(max, timeout, next) {
request('http://0.0.0.0:9200', function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('fail');
if (count++ < max) {
return setTimeout(function() {
retry(max, timeout, next);
}, timeout);
} else {
return next(new Error('max retries reached'));
}
}
console.log('success');
next(null, body);
});
}
})();
retry(20, 1000, function(err, body) {
do(something);
});
You can set a max number of retries and a timeout between retries. So that you do not introduce an infinite loop, and you do not deliver the final punch to an overloaded request target ^^

I wanted a little more intuitive answer including promises. I build on top of miggs answer within a try/catch the code below with promises and axios.
Based on a simple example of recursive functions
const throwNumbers = (count = 0) => {
console.log(count);
if (count++ < 10) {
throwNumbers(count);
} else {
console.log('max reached');
};
};
You can put anything else on the try part and handle error codes in the catch part. You have to set a max number of retries, which is 10 in my case.
let getResponse = async(count = 0) => {
try {
const axiosResponse = await axios.get(someURL, {
params: {
parameter1: parameter1,
},
});
return axiosResponse;
} catch (error) {
if (error || error.status != 200) {
console.error('failed, retry');
if (count++ < 10) {
return getResponse(count);
} else {
throw new Error('max retries reached');
};
} else {
throw error;
};
};
};
You would call the function with the following and handle the body or whatever with the response value.
let response = await getResponse();
console.log('This is the response:', response);
Has no timeout but works for me.

Related

ensure code is running twice with condition

I have code which needs to do request to some server with token,
Sometimes you need to run the code twice to get the data. (the exact same code )
How can I ensure that the code will run only twice and not recursively ?
Most of the time in the first run I got http response 401 and the second run give http 200
This is the code
async function magt() {
let auth: any;
try {
auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
...
//run rest of the code
} else {
console.log("unable to fetch tokens")
if (auth.response.status == 401) {
console.log("Running for the second time to get valid token")
await magt()
}
}
} catch (e) {
console.log("error occurred while fetching token: ", e)
}
I want to make sure that if I got always 401, run the magt() func only twice, what is the best way to do it ?
is there a better way from some simple counter which is defined by global?
How about a simple for loop?
async function magt() {
for (var attempt = 0; attempt < 2; attempt++) {
try {
let auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
return true;
}
console.log("Invalid auth response:", auth.response);
} catch (e) {
console.log("error occurred while fetching token: ", e);
}
}
throw new Error("All attempts at magt'ing failed miserably");
}
async function something() {
await magt();
//"run rest of the code"
}
Or, if you're feeling fancier, write a wrapper function for retrying async functions:
async function retrying(func, attempts) {
for (let attempt = 0; attempt < attempts; attempt++) {
try {
return await func();
} catch (err) {
console.log(func, "attempt", attempt, "failed:", err);
}
}
throw new Error("retry failed");
}
async function checkToken() {
const auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
return auth;
}
throw new Error("invalid auth response: " + auth.response);
}
async function magt() {
const token = await retrying(checkToken, 2);
}
async function magt(iterationLeft) {
let auth: any;
iterationLeft--;
try {
auth = await getTokens();
if (auth.runtime?.status == 200 && auth.application?.status == 200) {
...
//run rest of the code
} else {
console.log("unable to fetch tokens")
if (auth.response.status == 401 && iterationLeft) {
console.log("Running for the second time to get valid token")
await magt(iterationLeft)
}
}
} catch (e) {
console.log("error occurred while fetching token: ", e)
}
Calling with number of iteration as argument magt(2);

NodeJS Request return JSON from function

I've read a couple of posts about this here (callbacks) but I still don't really fully understand how to solve my problem. So I was hoping that somebody here could help me with mine and I would get it better.
Simple put I want the ID I get from the first request to be used for the second request.
I'm new to JavaScript and NodeJS in general.
function idRequest(name) {
var options = {
...
};
function callback(error, response, body) {
if (response.statusCode == 200 && !error) {
const info = JSON.parse(body);
//console.log(info.accountId);
return info.accountId;
}
}
request(options, callback);
}
function requestById(accountId) {
var options = {
...
};
function callback(error, response, body) {
if (response.statusCode == 200 && !error) {
const info = JSON.parse(body);
console.log(info);
}
}
request(options, callback);
}
var id = idRequest('..');
requestById(id);
Try by returning a promise from the first function and inside it resolve the callback, so the once it is resolved , you can use it's then to trigger the second function
function idRequest(name) {
var options = {
...
};
function callback(error, response, body) {
if (response.statusCode == 200 && !error) {
const info = JSON.parse(body);
//console.log(info.accountId);
return info.accountId;
}
}
return new Promise(function(resolve, reject) {
resolve(request(options, callback))
})
}
function requestById(accountId) {
var options = {
...
};
function callback(error, response, body) {
if (response.statusCode == 200 && !error) {
const info = JSON.parse(body);
console.log(info);
}
}
request(options, callback);
}
var id = idRequest('..').then(function(data) {
requestById(data);
});
since callback is a async call, so var id will be undefined, when you call the requestById(id);
so either you can use the promise method, answered by #brk or you can call your requestById(id) function directly from the first callback.

Retry promise himself after fail in node js

I would like to retry my request in a promise. I would like launch my refresh if I have always an 401 error as a loop : (if I have 401 loop on refresh until 200)
I tried with this :
const request = require('request');
let conf = require('../conf');
let core_service = require('coreService');
let self = module.exports = {
get_count_questions: function() {
return new Promise((resolve, reject) => {
request({
method: 'GET',
uri: 'http://api/count-questions',
auth: {
'bearer': conf.token
},
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
resolve(body);
} else if (!error && response.statusCode === 401) {
core_service.refreshToken().then((data) => {
console.log('token refresh');
return self.get_count_questions();
})
} else {
reject(error);
}
})
});
}
};
I tried with just 'self.get_count_questions();' without return, but it's not work. I have not error message, just my app freeze.
I see in my console.log "token refresh", but after my app freeze...
Edit
I modified with this, It's like better but the refresh token it's very slow. Just before 401, my app stop, and after about 1 minutes 40 seconds, run:
else if (!error && response.statusCode === 401) {
console.log('need refresh token');
core_service.refreshToken()
.then((response) => {
console.log(response);
resolve(self.get_count_questions())
} );
}
My refreshToken function :
refreshToken: function () {
return new Promise((resolve, reject) => {
request({
method: 'GET',
uri : 'http://api/refresh',
auth : {
'bearer': conf.token
},
json : true
}, function (error, response, body) {
console.log('=====> refresh token <======');
conf.token = body.data;
console.log('new Token');
console.log('=====> end refresh token <======');
if (!error && response.statusCode === 200) {
resolve('Refresh token successful');
} else {
reject('Error refresh');
}
})
});
}
If I refresh my token on each request, I have a problem :
if (!error && response.statusCode === 200) {
core_service.refreshToken().then((data)=> {
resolve(body);
});
}
You have to resolve the returned promise. When you resolve using a promise, you basically say, complete this promise with the result of that promise.
var prom = function() {
return new Promise((resolve, reject) => {
console.log('request start')
setTimeout(() => {
console.log('request finish')
let ran = Math.random();
if (ran < 0.1)
resolve('success');
else if (ran >= 0.1 && ran < 0.98)
setTimeout(() => {
console.log('retry');
resolve(prom());
}, 500);
else
reject('error');
}, 500);
});
};
prom().then(console.log.bind(console), console.log.bind(console));
So you should update your else if block like this:
else if (!error && response.statusCode === 401) {
console.log('need refresh token');
core_service.refreshToken()
.then(() => resolve(self.get_count_questions()));
}
You're making a recursive call, but you're never actually doing anything with its promise. Therefore, your original promise never resolves.
You need to pass the promise from the recursive call (to refreshToken().then()) to resolve().
Now you almost have it.
However:
return core_service.refreshToken()
.then(self.get_count_questions);
You're returning that to the request() callback; that return value is not used.
Instead, you need to resolve your original promise to the new promise from then(), by passing it to your original resolve() function:
resolve(core_service.refreshToken().then(...));
I know that is not optimal solution but it might helps
const request = require('request');
let conf = require('../conf');
let core_service = require('coreService');
let self = module.exports = {
get_count_questions: function() {
return new Promise((resolve, reject) => {
request({
method: 'GET',
uri: 'http://api/count-questions',
auth: {
'bearer': conf.token
},
json: true
}, function (error, response, body) {
try{
if (!error && response.statusCode === 200) {
resolve(body);
} else if (!error && response.statusCode === 401) {
throw new Error(response.statusCode);
} else {
reject(error);
}
}catch(exc){if(exc === 401){
core_service.refreshToken().then((data) => {
console.log('token refresh');
return self.get_count_questions();
})
}
}
})
});
}
};
You need to call the initial resolve/reject functions after you retried the request:
let self = module.exports = {
get_count_questions: function() {
return new Promise((resolve, reject) => {
request({
method: 'GET',
uri: 'http://api/count-questions',
auth: {
'bearer': conf.token
},
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
resolve(body);
} else if (!error && response.statusCode === 401) {
core_service.refreshToken().then((data) => {
console.log('token refresh');
self.get_count_questions().then((data) => {
// call initial resolve function
resolve(data);
}).catch((error) => {
// call initial reject function
reject(error);
});
}).catch((error) => {
// reject if refreshToken fails
reject(error);
});
} else {
reject(error);
}
})
});
}
};
You also have to make sure, that the second call actually resolves/rejects and doesn't land in another 401. Because else you have an infinite recursion.

Chaining multiple request using bluebird

I'm trying to convert my existing code using BlueBird, please suggest a best option to chain multiple request. Error happening in each callback needs to be redirected to rendered with different error.
request(option1, function (error, response, body) {
if (!error && response.statusCode == 200) {
var data= JSON.parse(body);
if(data.valid){
if(data.expired){
next();
} else {
request(option2, function (error2, response2, body2) {
var data2= JSON.parse(body2);
if(data2.valid) {
request(option3, function (error3, response3, body3) {
next();
})
} else {
res.json({error:'Error1'});
}
})
}
} else {
res.json({error:'Error2'});
}
} else {
res.json({error:'Error3'});
}
})
This is pretty straightforward, also note your current code doesn't handle errors in the second and third requests and this does:
var request = require("request-promise"); // request - converted to bluebird
request(option1).then(data=> {
if(!data.valid) throw Error("Error3");
if(data.expired) return;
return request(option2).then(JSON.parse);
}).then(data2 => {
if(!data2) return; // didn't need to fetch additional data
if(!data2.valid) throw Error("Error2");
return request(option3);
}).then(() => {
next();
}, e => {
res.json(error: e.message);
// better log this.
});
var rp = require('request-promise');
function handleError(err) {
res.json({
error: err.message
});
}
function parse(data) {
if (data) {
return JSON.parse(data);
}
}
rp(option1)
.then(parse)
.then(function (data) {
if (!data || !data.valid) {
throw Error('Error2');
}
if (data.expired) {
return;
}
return option2;
})
.then(rp)
.then(parse)
.then(function (data2) {
if (!data2 || !data2.valid) {
throw Error('Error1');
}
return option3;
})
.then(rp)
.then(parse)
.then(function () {
next();
})
.catch(handleError);
You don't need to manually check for statusCode but if you need to do so, first you have to add resolveWithFullResponse attribute to your option1 object, which allows you to receive the response object:
function checkStatusCode(response) {
if (response.statusCode !== 200) {
throw Error('Error3');
}
return response.body;
}
// add resolveWithFullResponse attribute to option1
option1.resolveWithFullResponse = true;
rp(option1)
.then(checkStatusCode)
.then(parse)
//...

Bluebird Promise Retry DocumentDB request

I'm trying to rewrite a retry function with callbacks into a Bluebird promise one but can't seem to get my head around the correct way of doing this. At the bottom is the working callback function for retrying Azure DocumentDB when limit is met. I'm trying to use promises in the function itself but it returns before reaching the "Then". Any hints on how to tackle this or if performance is affected by using catch this way would be appreciated. Thank you!
"readDocRetry": function(id, retries) {
var self = this;
return new Promise(function(resolve, reject){
self.client.readDocumentAsync(self.docsLink + id, null, function(err, data){
if (err) {
reject(err);
} else {
resolve(data)
}
}).then(function(results) {
console.log("ReadDocRetry result: " + results)
return results;
}).catch(function(err, headers) {
RetryError(self, id, err, headers, retries);
});
});
}
function RetryError(self, id, err, headers, retries) {
if (err && err.code) {
if (err.code === 429 && retries >= 0) {
setTimeout(function() {
self.readDocRetry(id, retries - 1);
}, Number(headers['x-ms-retry-after-ms'] || 1));
}
else if (err.code === 503 && retries >= 0) {
setTimeout(function() {
self.readDocRetry(id, retries - 1)
}, 500);
}
}
else if(err) {
console.log(err);
}else{
console.log("Err missing in RetryError");
}
}
bbCtx.readDocRetry("19").then(function(res){
console.log("Hurrah!" + res);
})
------- Working example with traditional callbacks which I'm trying to make promise based -----
dbContext.prototype = {
readDocRetry: function (id, retries, cb) {
var self = this;
self.client.readDocument(self.docsLink + id, function (err, results, headers) {
if (err) {
if (err.code === 429 && retries >= 0) {
var aR = retries - 1;
setTimeout(function () {
self.readDocRetry(id, aR, cb);
}, Number(headers['x-ms-retry-after-ms'] || 1));
} else if (err && err.code === 503 && retries >= 0) {
var aR = retries - 1;
setTimeout(function () {
self.readDocRetry(id, aR, cb)
}, 500);
} else {
cb(err);
}
} else {
cb(null, results);
}
});
},
When your catch callback is supposed to handle anything, it will need to return that new result like every other promise callback. In your case, it could return the promise for the result of the retry attempt:
function readDocRetry(id, retries) {
var self = this;
return new Promise(function(resolve, reject){
self.client.readDocumentAsync(self.docsLink + id, null, function(err, data){
if (err) {
reject(err);
} else {
resolve(data)
}
});
}).then(function(results) {
console.log("ReadDocRetry result: " + results)
return results;
}).catch(function(err, headers) {
if (err && err.code) {
if (err.code === 429 && retries >= 0) {
return Promise.delay(headers['x-ms-retry-after-ms'] || 1).then(function() {
return self.readDocRetry(id, retries - 1);
});
} else if (err.code === 503 && retries >= 0) {
return Promise.delay(500).then(function() {
return self.readDocRetry(id, retries - 1)
});
}
}
if (err) {
console.log(err);
throw err;
} else {
console.log("Err missing in RetryError");
throw new Error("rejection without error");
}
});
}

Categories

Resources