Search code examples
javascriptajaxvue.jsvue-resource

Retry vue resource ajax calls using recursion


I'm trying to have my ajax call retry if it fails initially. It does it 3 times each time if the initial ajax call fails with a timeout of 5 seconds. The function is being imported on my vue component as a method and being called on ready()

export function getOrderStatus (order) {
  let url = `/orders/status/${order.id}`
  let promise = this.$http.get(url)

  function retry(order, path, promise, retryAttempt) {
    promise.then((response) => {
      if (response.body.status === 'success') {
        showOrderStatus()
      }
    }, (response) => {
      if (retries < 2) {
        retryAttempt++
        setTimeout(retry(order, path, promise, retryAttempt), 5000);
      } else {
        handleError()
      }
    })
  }

  retry(order, path, promise, 0)
}

Component

  import { getOrderStatus } from '../shared'

  export default {
   name: 'order-page',
   props: { order },
   methods: { getOrderStatus },
   ready() {
     this.getOrderStatus(order)
   }
 }

I'm not sure if this is the best way to go about retrying ajax calls so any advice would be appreciated.


Solution

  • You will need to refactor this because you are caching promise. The issue here is that Promises by nature will only complete once, resolving or rejecting. Therefore if your $http request does fail, your future calls to retry() will all also fail without calling the endpoint.

    Try something like below:

    component could be refactored to a mixin if you want to share it across components (rather than import { getOrderStatus } from '../shared')

    data () {
        return {
            attempt: 0,
        }
    }
    
    methods: {
    
        showOrder () { // or whatever needs this
    
            this.getOrderStatus('order-123')
                .then((reponse) => this.showOrderStatus())
                .catch((reponse) => this.handleError(response))
    
        },
    
        getOrderStatus (order) {
    
            this.attempt = 0
    
            return 
                new Promise((resolve, reject) => this.callOrderEndpoint({
                    order,
                    resolve,
                    reject,
                }))
    
        },
    
        callOrderEndpoint ({ order, resolve, reject }) {
    
            const url = `/orders/status/${order.id}`
    
            this.$http
                .get(url)
                .then((response) => {
                    if (response.body.status === 'success') {
                        resolve()
                    } else {
                        reject()
                    }
                })
                .catch(response) => {
                    if (this.attempt++ < 2) {
                        setTimeout(() => 
                            this.callOrderEndpoint({ order, resolve, reject }), 
                            5000))
                    } else {
                        reject(response)
                    }
                })
    
        },
    
        showOrderStatus () {
    
            // whatever this does
    
        },
    
        handleError (response) {
    
            console.error('fail')
    
        },
    
    },
    

    I thought a better way to go would be to return a Promise from getOrderStatus. This would allow you to move your success / fail methods to the then / catch methods for better structure.