ES6 - Conditional repeat signaling in Promise until the maximum number of attempts is reached

I saw a few issues related to Promises repetition, however, what I want to do is slightly different in that I would like to control the retry / rejection of promises conditionally until the max reps are reached.

To give a simple example, imagine that we are assuring a promise around XMLHttpRequest. When a request loads with status ...

  • 200: resolve promise
  • 299: try again immediately
  • 399: cancel immediately
  • 499: get something from the server, and then try again

Note that there is scope for asynchronous behavior that must be performed before repeating.

The solution I reviewed includes two Promises.

  • Firstly, it is a wrapper around each attempt and makes a simple decision / rejection based on the result of this attempt.
  • Secondly, it is a shell around many attempts, which conditionally discards individual promises.

Going back to the example I mentioned ...

  • The first promise rules everyone XMLHttpRequest, resolving a condition 200and refusing otherwise.
  • The second promise resolves itself when any attempt is permitted. Whenever an attempt is rejected, it makes a decision about the next action (retry, reject, retrieve, retry, etc.) Based on this attempt status code.

I think that with this I will go in the right direction, but I can’t find a concrete solution. I am looking to create a common wrapper for this kind of "shareware promises."


Edit:

Here is the solution:

async function tryAtMost(maxAttempts, asyncCall, handleError)
{
    for (let i = 0; i < maxAttempts; i++)
    {
        try 
        { 
            return await asyncCall(); 
        }
        catch (error)
        {
            const nextAction = await handleError(error); // await some async request (if available) before proceeding
            const actionError = new Error(nextAction.error);

            switch (nextAction.type)
            {
                case ACTIONS.ABORT:
                    throw actionError;
                case ACTIONS.RETRY:
                    if (i === maxAttempts - 1) { throw actionError; }
                    else { continue; }
            }
        }
    }
}
+4
3

, . . , -

async function fetchWithRetries(theURL, remainingRetries = 5) {
  const response = await fetch(theURL);

  switch (response.status) {
    case 200:
      return await response.json(); // or whatever you need
    case 299:
      if (remainingRetries === 0) {
        throw new Error();
      }
      return await fetchWithRetries(theURL, remainingRetries - 1);
    case 399:
      throw new Error();
    case 499:
      if (remainingRetries === 0) {
        throw new Error();
      }

      const otherData = await fetchOtherData();

      return await fetchWithRetries(theURL, remainingRetries - 1);

    default:
      // TODO: You didn't specify other codes?
  }
}
+2

, async ( Promise).

  • Class attempts.
  • async - , maxAttempts.
  • - , .
  • , maxAttempts.

Node.js request-promise-native:

const rp = require('request-promise-native')

class RetryableFetch {
  constructor({ url, maxAttempts = 3 }) {
    this.url = url
    this.maxAttempts = maxAttempts    
    this.attempts = 0

    return this.generateRequest()
  }

  async generateRequest() {
    for (let i = 0; i < this.maxAttempts; i++) {
      try {
        return await rp(this.url)
      } catch(err) {
        switch (err.statusCode) {
          // Add more cases here as you see fit.
          case 399:
            throw err
            break;
          default:
            if (++this.attempts === this.maxAttempts) throw err
        }
      }
    }
  }
}

:

new RetryableFetch({
  url: 'https://www.google.com'
})
.then(result => {
  console.log(result)
})
.catch(err => {
  console.error(err)
})

, , rp Fetch, , , Promise- API.

+1

:

"" .

:

  • max.
  • Promise.
  • , , , max .

// Class Retryable

class Retryable {
  constructor({ promise, maxAttempts = 1, attemptRetry }) {
    this.promise = promise
    this.maxAttempts = maxAttempts
    this.attemptRetry = attemptRetry

    this.attempts = 0
  }

  generateTry() {
    console.info('generating request')

    return this.promise().catch(err => {
      if (++this.attempts === this.maxAttempts) throw err

      return this.attemptRetry(err, () => this.generateTry() , () => {
        throw err
      })
    })
  }
}

// Usage

const retryable = new Retryable({
  maxAttempts: 4,
  promise: () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject({ status: 500 })
        // If you `resolve` here instead you will trigger `.then()`
      }, 200)
    })
  },

  attemptRetry: function(err, yes, no) {
    switch (err.status) {
      case 500:
        return yes()
        break;
      default:
        return no()
    }
  }
})

retryable.generateTry().then(result => {
  console.log(result)
}).catch(err => {
  console.error(err)
})
Hide result
+1

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


All Articles