휘바골드
휘바골드

Reputation: 15

How can I retry function?

I'm developing a script for personal needs. I need to retry if an error returned, but I don't know how to fix it. how to resolve it?

const request = require("request");
const co = require('co');


function init() {
  co(function *(){
    var res = yield GetData();
    console.log(res);
  });
}

function onerror(error) {
  console.log("error below");
  console.log(error);
  console.log(error.code);
  console.error(error.stack);
}

function send_request(options, callback){
  co(function *(){
    let RetryAttemptCount = 0;
    let MaxRetries = 3;

    let res;

    res = yield request(options, function (error, response, body) {
      var tmp;
      if (!body && error && !response) {
        RetryAttemptCount++;
        console.log('increase RetryAttemptCount :',RetryAttemptCount);
        throw new onerror(error);
      } else if (!error) {
        tmp = JSON.parse(body);
        return tmp;
      }
    });
  }).catch(onerror);
}

function GetData() {
  return new Promise(function(resolve, reject){
    var options = { method: 'GET', url: 'https://api.upbit.com/v1/market/all' };

    send_request(options, (res) => {
      resolve(res);
    });
  });
}

init();

But I get the following error:

TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "[object Object]"

Upvotes: 1

Views: 2260

Answers (3)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276306

You can do it very generally with a simple retry function:

function retry(fn, attempts = 3, delay = 2000) {
    return async function(...args) {
        for(let i = 0; i < attempts; i++) {
            try {
            await fn.call(this, ...args); // delegate
            } catch (e) {
                if(attempts > 0) await new Promise(r => setTimeout(r, delay));
                else throw e;
            }
        }
    }
}

This would let you do:

let retried = retry(fn);
// ... then later

await retried({ ... params });

Upvotes: 2

jogo
jogo

Reputation: 111

Thanks to @Benjamin's answer I came up with this modified code which is a bit simplified plus adds exponential backoff stuff.

function retry(fn, retries = 5, interval = 2000, backOff = 2) {
  return async function(...args) {
    let delay = interval;
    do {
      try {
        console.log('Trying...');
        return await fn(...args);
      } catch (e) {
      	console.error(e['message']);
        retries--;
        if (retries > 0) {
          console.log('Waiting ' + delay + 'ms...');
          await new Promise(resolve => setTimeout(resolve, delay));
          delay *= backOff;
        } else {
          throw e;
        }
      }
    } while (true);
  }
}

let x = 0;
function mightFail(value) {
  x++;
  if (x < 5) {
    throw new Error('transient error occurred');
  }
  return value;
}

async function test() {
  try {
    const result = await retry(mightFail)(12345);
    console.log('Success: ' + result);
  } catch (e) {
    console.error('Failure: ' + e);
  }
}

test();

Upvotes: 1

Milad Aghamohammadi
Milad Aghamohammadi

Reputation: 1976

I suggest you use requestretry npm instead of request. It's simple to use

var request = require('requestretry');

request({
url: 'https://api.domain.com/v1/a/b',
json: true,

// The below parameters are specific to request-retry
maxAttempts: 5,   // (default) try 5 times
retryDelay: 5000,  // (default) wait for 5s before trying again
retryStrategy: request.RetryStrategies.HTTPOrNetworkError // (default) retry on 5xx or network errors
}, function(err, response, body){
// this callback will only be called when the request succeeded or after maxAttempts or on error
if (response) {
    console.log('The number of request attempts: ' + response.attempts);
}
});

You can control retry count with changing maxAttempts value

Upvotes: 0

Related Questions