Retry the async function if the error persists

How can I change my logic to retry if err.retryable = true in the following code:

async.each(queues, function (queue, callback) {
    sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
        if (err) {
            if (err.retryable) {
                // How to retry  sqs.getQueueUrl({'QueueName': queue.queue}...?
            } else {
                console.error(err, err.stack);
                callback(err);
            }
        }else{
            //Do lots of things here
        }        

    })
}, function (err) {
    //...
})

      

+3


source to share


2 answers


You can give the call a queue name and re-include it in the retry request. Try the following:



async.each(queues, function (queue, callback) {
    sqs.getQueueUrl({'QueueName': queue.queue}, function queueCallback(err, qurl) {
        if (err) {
            if (err.retryable) {
                sqs.getQueueUrl({'QueueName': queue.queue}, queueCallback);
            } else {
                console.error(err, err.stack);
                callback(err);
            }
        } else {
            //Do lots of things here
        }
    });
}, function (err) {
    //...
});

      

+1


source


In addition to dfsq's advice to name your callback and use it asynchronously in a recursive manner, see also async.retry from async by Caolan McMahon. Example:

async.retry(3, apiMethod, function(err, result) {
    // do something with the result
});

      

More complex example:

async.auto(
  {
    users: api.getUsers.bind(api),
    payments: async.retry(3, api.getPayments.bind(api))
  }, function(err, results) {
    // do something with the results
  }
);

      

More details in the docs .

UPDATE

Best solution for your use case:

I wrote a utility function that you can use to make your original method support any number of attempts (with support err.retryable

).

You can use it like this:

var retryingFunction = withRetries(sqs, sqs.getQueueUrl);

      

(Please note that you need to provide both sqs

and sqs.getQueueUrl

)



And now you can use retryingFunction

it just as you would sqs.getQueueUrl

, but with a number of tries as the first arguments. Repetitions are performed only when err.retryable

true.

So instead of:

sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
  // ...
});

      

you can use:

retryingFunction(3, {'QueueName': queue.queue}, function (err, qurl) {
  // ...
});

      

where 3 is the number of attempts.

And this is the function I wrote to do my best:

function withRetries(obj, method) {
  if (!method) {
    method = obj;
    obj = null;
  }
  if (typeof method != "function") {
    throw "Bad arguments to function withRetries";
  }
  var retFunc = function() {
    var args = Array.prototype.slice.call(arguments);
    var retries = args.shift();
    var callback = args.pop();
    if (typeof retries != "number" || typeof callback != "function") {
      throw "Bad arguments to function returned by withRetries";
    }
    var retryCallback = function (err, result) {
      if (err && err.retryable && retries > 0) {
        retries--;
        method.apply(obj, args);
      } else {
        callback(err, result);
      }
    };
    args.push(retryCallback);
    method.apply(obj, args);
  };
  return retFunc;
}

      

Watch this LIVE DEMO to play with it and see how it works.

It works great in the demo, I hope it works for your code as well.

+3


source







All Articles