How can you retry after an exception in Javascript when using promises?

I use the Bluebird promise library. I have a chain of multi-valued functions, such as:

receiveMessageAsync(params) .then(function(data)) { return [data, handleMessageAsync(request)]; }) .spread(function(data, response) { return [response, deleteMessageAsync(request)]; }) .spread(function(response, data) { return sendResponseAsync(response); }) .then(function(data) { return waitForMessage(data); }) .catch (function(err) { // handle error here }); 

Sometimes sendMessage fails because, say, the server is unavailable to respond to it. I want the code to keep trying to answer forever until it succeeds. You cannot just wrap sendMessage in catch because it does not actually throw an exception, I suppose it calls the "error" function, which in this promising code is "catch" below. Therefore, in the "catch" section there must be some way to "retry" sending the message. The problem is that even if I repeat the loop in “catch”, I still have no way to go to the promise chain and fulfill the remaining promised functions. How can I handle this?

EDIT:

My attempt for an HTTP header looked like this:

 function retry(func) { return func() .spread(function(httpResponse) { if (httpResponse.statusCode != 200) { Log.error("HTTP post returned error status: "+httpResponse.statusCode); Sleep.sleep(5); return retry(func); } }) .catch(function(err) { Log.err("Unable to send response via HTTP"); Sleep.sleep(5); return retry(func); }); } 
+6
source share
4 answers

Here's an example snooze function (not tested yet):

 function retry(maxRetries, fn) { return fn().catch(function(err) { if (maxRetries <= 0) { throw err; } return retry(maxRetries - 1, fn); }); } 

The idea is that you can wrap a function that returns a promise with something that will catch and repeat the error until the retry ends. Therefore, if you are going to repeat sendResponseAsync :

 receiveMessageAsync(params) .then(function(data)) { return [data, handleMessageAsync(request)]; }) .spread(function(data, response) { return [response, deleteMessageAsync(request)]; }) .spread(function(response, data) { return retry(3, function () { return sendResponseAsync(response); }); }) .then(function(data) { return waitForMessage(data); }) .catch (function(err) { // handle error here }); 

Since the promise of retry will not actually be thrown out until all attempts have been exhausted, your call chain may continue.

Edit

Of course, you can always loop forever if you want:

 function retryForever(fn) { return fn().catch(function(err) { return retryForever(fn); }); } 
+7
source

Here is a little helper that acts like then , but repeats the function.

 Promise.prototype.retry = function retry(onFulfilled, onRejected, n){ n = n || 3; // default to 3 retries return this.then(function(result) { return Promise.try(function(){ return onFulfilled(result); // guard against synchronous errors too }).catch(function(err){ if(n <= 0) throw err; return this.retry(onFulfilled, onRejected, n - 1); }.bind(this)); // keep `this` value }.bind(this), onRejected); }; 

That allows you to write code more beautiful, for example:

 receiveMessageAsync(params) .then(function(data)) { return [data, handleMessageAsync(request)]; }) .spread(function(data, response) { return [response, deleteMessageAsync(request)]; }) .retry(function(response, data) { return sendResponseAsync(response); // will retry this 3 times }) .then(function(data) { return waitForMessage(data); }) .catch (function(err) { // I don't like catch alls :/ Consider using `.error` instead. }); 
+2
source

I just released https://github.com/zyklus/promise-repeat , which repeats the promise until the time runs out or the maximum number of attempts hit. This allows you to write:

 receiveMessageAsync(params) ... .spread(retry( function(response, data) { return sendResponseAsync(response); } )) ... 
0
source

You can use this open-source promise-retry component that repeats a function that returns a promise.

Example:

 promiseRetry((retry, number) => promiseFunction().catch(retry),{retries:3}) .then((result) => console.log(result)).catch(err => console.log(err)) 

Here's how it works:

 const retry = require('retry'); const isRetryError = (err) => (err && err.code === 'RETRYPROMISE'); const promiseRetry = (fn, options) => { const operation = retry.operation(options); return new Promise( (resolve, reject) => { operation.attempt( (number) => { return fn(err => { if (isRetryError(err)) { err = err.retried; } throw {err:'Retrying', code:'RETRYPROMISE', message: err.message}; }, number) .then(resolve, (err) => { if (isRetryError(err)) { if (operation.retry(err || new Error())) { return; } } reject(err); }); }); }); } 

You can also use this NPM package for the job.

0
source

Source: https://habr.com/ru/post/987967/


All Articles