I got a function which makes a request to an API. Sometimes the API got some hiccups and isnt available for a second or two every now and then, resulting in an error on which I'd like to call the function again. Since there are another 70~80 lines of code following this callback, I wouldnt like to split the flow with an if(error) <do the same stuff> else <as here>
After trying for quite some time I ended up using a do-while(error) loop, which works but blocks. Is there an async way of doing this?
My code (simplified for generalization):
//This is the request part
function bar(j, callback){
j++;
//simulated error
if(j<=10){
return( callback('dont', j) );
}
//simulated success
else{
return( callback(null, j) );
}
}
//This is the main function - part of a bigger piece in my code
function foo(){
var i = 0;
var err = 'yes'; //else we'd get an 'err is not defined'
do{
bar(i, function(error, j){
i = j
err = error;
if(error){
console.log(i);
}
else{
return( console.log('done it!') );
// There's more here in my code
}
});
} while (err);
console.log('I blocked');
}
foo();
Edit:
For those interested, this is the output:
1
2
3
4
5
6
7
8
9
10
done it!
I blocked
What I would suggest is that you make a function for your operation. If it fails, you set a short timer and retry after that timer fires. This will give you an asynchronous behavior between retries and other code in the sever can run.
function requestRetry(url, data, retryTimes, retryDelay, callback) {
var cntr = 0;
function run() {
// try your async operation
request(..., function(err, data) {
++cntr;
if (err) {
if (cntr >= retryTimes) {
// if it fails too many times, just send the error out
callback(err);
} else {
// try again after a delay
setTimeout(run, retryDelay);
}
} else {
// success, send the data out
callback(null, data);
}
});
}
// start our first request
run();
}
requestRetry(someUrl, someData, 10, 500, function(err, data) {
if (err) {
// still failed after 10 retries
} else {
// got successful result here
}
});
This is a fairly simple retry scheme, it just retries on a fixed interval for a fixed number of times. More complicated schemes implement a back-off algorithm where they start with fairly quick retries, but then back-off to a longer period of time between retries after the first few failures to gives the server a better chance of recovering. If there happening to be lots and lots of clients all doing rapid retries, then you as soon as your server has a hiccup, you can get an avalanche failure as all the clients suddenly start rapidly retrying which just puts your serve in even more trouble trying to handle all those requests. The back-off algorithm is designed to allow a server a better chance of preventing an avalanche failure and make it easier for it to recover.
The back-off scheme is also more appropriate if you're waiting for the service to come back online after it's been down a little while.
For retry http call when it come with error. but first you need to check this error be retry-able or not.
RETRIABLE_NETWORK_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'];
If error comes under RETRIABLE_NETWORK_ERRORS
than you need to come under retry logic otherwise mark as error.
For retry logic use exponential backoff algorithm. you follow https://developers.google.com/api-client-library/java/google-http-java-client/backoff
const _ = require('lodash');
const request = require('request');
var httpArray = getHttpReq(); //return array
function makeHttpRequest() {
_.each(httpArray, function (httpRequest) {
retryRequest(httpRequest);
});
}
function retryRequest(httpRequest) {
const MAX_RETRY = 2;
var retryCnt = 0;
Retry();
function Retry() {
request(httpRequest, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body)
}
else {
if (retryCnt < MAX_RETRY) {
retryCnt += 1;
var currRetryIntervalMs = (1 << retryCnt) * 1000; //exponential back off logic
setTimeout(Retry, currRetryIntervalMs);
}
else {
console.log('http fail');
}
}
});
}
}
Here is an asynchronous loop
function asyncLoop(i, range, callback) {
var results = 0;
if(i < range) {
// do something, update results
aysncLoop(i+1, range, callback);
} else {
callback(null, results)
}
}
To loop it 10 times, call it as follows
asyncLoop(0, 10, function(err, results) {
console.log(results);
});
Pass your error condition in place of range and check it inside the loop. Hope this will help you
来源:https://stackoverflow.com/questions/35195725/nodejs-re-calling-function-on-error-callback-is-there-a-non-blocking-way