cvDv cvDv - 8 days ago 5
Ajax Question

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.

Answer

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.