首页 > 解决方案 > How would you automatically retry a failed ajax call 3 times (synchronously)?

问题描述

I'm trying to rerun a failed AJAX call 3 times. After the third attempt, I'd like to call a failed method. I don't want the AJAX calls to over run each other though.

What's the safest/best way to achieve this with what I'm working with?

I'm using a globalAjaxRequest method like so:

globalAjaxRequest(request, successCallback, errorCallback) {
        let ajaxRequest = null;

        if (request.url) {
            const ajaxOptions = {
                type: request.method ? request.method.toUpperCase() : 'POST',
                url: request.url,
                data: request.data || undefined,
                beforeSend: request.beforeSend,
                success: (data) => {
                    successCallback(data);
                },
                error: (data) => {
                    if (errorCallback) {
                        errorCallback(data);
                    }
                }
            };

            ajaxOptions.dataType = request.dataType || 'json';
            ajaxOptions.contentType = request.contentType || 'application/json; charset=utf-8';

            if (request.contentType) {
                ajaxOptions.data = $.parseJSON(JSON.stringify(ajaxOptions.data));
            } else {
                ajaxOptions.data = JSON.stringify(ajaxOptions.data);
            }

            ajaxRequest = $.ajax(ajaxOptions);
        }

        return ajaxRequest;
    }
}

Here's my attempt:

callAPI() {
    const callData = {
        url: '/callApi',
        data: {
            id: 'something'
        }
    };

    global.Utils.globalAjaxRequest(callData, (success) => {
        console.log('success');
        successMethod();
    }, (fail) => {
        for (let i = 1;; i++) {
            i <= 3 && setTimeout(() => {
                callAPI();
            }, 1000);

            if (i > 3) {
                failedMethod();
                break;
            }
        }
    });
}

callAPI();

标签: javascriptajaxecmascript-6

解决方案


您不能重试异步操作,例如$.ajax()同步,所以我假设您只是想在失败时自动按顺序重试。

这是一个通用的重试函数$.ajax()

// general purpose promise delay, useful when you want to delay a promise chain
function pDelay(t, v) {
    return new Promise(function(resolve) {
        setTimeout(resolve, t, v);
    });
}

// three arguments:
//   options: value for $.ajax(options) - does not support other forms of calling $.ajax()
//   delay: amount of time in ms to delay before each retry (can be 0 if you want)
//   retries: number of times to retry, defaults to 3 if you don't pass it
$.ajaxRetry = function(options, delay, retries) {
    // default value for retries is 3 if the argument is not passed
    let retriesRemaining = retriesRemaining !== undefined ? retriesRemaining: 3;

    let opts = Object.assign({}, options);

    function run() {
        return $.ajax(opts).catch(function(err) {
            --retriesRemaining;
            // don't fire this more than once
            delete opts.beforeSend;
            if (retriesRemaining > 0) {
                // try again after short delay
                return pDelay(delay).then(run);
            } else {
                // hit max retries, propagate error back to caller
                throw e;
            }
        });
    }
    return run();
}

仅供参考,此代码假定您的情况下的“失败”意味着$.ajax()拒绝的承诺。如果“失败”意味着其他东西(例如查看您得到的某些结果),那么您将不得不将该附加测试插入重试循环或公开一个回调,以便可以在外部提供该附加测试。


要将其集成到您的包装器中,您可以这样做:

globalAjaxRequest(request, successCallback, errorCallback) {
        let ajaxRequest = null;

        if (request.url) {
            const ajaxOptions = {
                type: request.method ? request.method.toUpperCase() : 'POST',
                url: request.url,
                data: request.data || undefined,
                beforeSend: request.beforeSend,
            };

            ajaxOptions.dataType = request.dataType || 'json';
            ajaxOptions.contentType = request.contentType || 'application/json; charset=utf-8';

            if (request.contentType) {
                ajaxOptions.data = $.parseJSON(JSON.stringify(ajaxOptions.data));
            } else {
                ajaxOptions.data = JSON.stringify(ajaxOptions.data);
            }

            errorCallback = errorCallback || function(err) { throw err; };
            ajaxRequest = $.ajaxRetry(ajaxOptions, 0, 3).then(successCallback, errorCallback);
        }

        return ajaxRequest;
    }
}

仅供参考,采用 Promise 接口并将其转换回普通回调有点奇怪。看来您应该摆脱successCallbackerrorCallback让调用者使用返回的承诺。


推荐阅读