AJAX - 同时运行两个请求,但其中一个需要以一定间隔重复执行

3
我希望实现以下功能: 第一个 AJAX 请求启动将数据导入到 MS SQL 数据库的过程。数据库中有一个包含有关导入的一些数据的表(总记录数、处理记录数、开始时间等)。在导入期间,表中的数据会不断更新。
第二个 AJAX 请求应该每 5 秒从表中获取更新后的数据,并以某种通知形式显示在页面上。
我面临的问题是:第一个请求开始后,需要几分钟才能执行完。同时,第二个请求每隔 5 秒钟被调用一次,但所有请求都要等到第一个请求完成后才会执行,它们都排队并具有“挂起”状态。我无法弄清楚问题所在。看起来我无法在第一个请求的 TTFB 期间启动第二个 AJAX 请求。
我还尝试使用 jQuery.when() 和 jQuery.then(),但我不知道如何在 jQuery.when() 中使用间隔进行 AJAX 调用。有没有办法实现我想要的功能?
这是我的代码,RunImport 函数在导入按钮单击时调用:
var resultCheck, resultRun, intervalId = 0;
    $(document).ready(function () {
        CheckActiveImports();
        
    });
    
    function CheckActiveImports() {
        var jqXHR = $.ajax({
            url: "exhaust.aspx/GetRunningImports",
            async:true,
            type: "GET",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            beforeSend: function(){
                $('#output').html("Checking for running imports...");
            },
            success: OnCheckSuccess,
            failure: function (result) {
                resultCheck = "Failure: " + result.responseText;
            },
            error: function (result) {
                resultCheck = "Error: " + result.responseText;
            },
            complete: function () {
                $('#output').html(resultCheck);
            }
        });
        return jqXHR;
    }

    function OnCheckSuccess(result)
    {
        var data = result.d; //JSON.parse(result).d;
        if (data.length) {
            var res = "";
            for (var x in data) {
                var format = 'Job running: ' + data[x].JobType + '. ' + data[x].Counter + ' of ' + data[x].Total + ' records processed<br /> \
                              Speed: ' + data[x].Speed + ' records per minute. \
                              Estimated time of completion: ' + data[x].EstTime + '.';
                res += format;
            }
            resultCheck = res;
        }
        else {
            resultCheck = 'Currently there are no jobs running.';
        }
    }

    function RunImport()
    {
        intervalId = window.setInterval(CheckActiveImports, 5000);
        RunImportAjax();
    }

    function RunImportDeferred() {
        
        var RunImportXHR = RunImportAjax(),
            CheckImportXHR = CheckActiveImports();
        $.when(RunImportXHR, CheckImportXHR).then(function () {
            $('#output').html(resultCheck);
            $('#output2').html(resultRun);
        });
    }

    function RunImportAjax() {
        var jqXHR = $.ajax({
            url: "exhaust.aspx/RunImport",
            async:true,
            type: "POST",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            beforeSend: function () {
                $('#output2').html("Running import...");
            },
            success: function (result) {
                window.clearInterval(intervalId);
                var data = result.d;
                if (data.length) {
                    var res = "";
                    for (var x in data) {
                        var format = 'Job finished: ' + data[x].JobType + '. ' + data[x].Counter + ' records processed.<br />';
                        res += format;
                    }
                    resultRun = res;

                }
            },
            failure: function (result) {
                resultRun = "Failure: " + result.responseText;
            },
            error: function (result) {
                resultRun = "Error: " + result.responseText;
            },
            complete: function () {
                $('#output2').html(resultRun);
            }
        });
        return jqXHR;
  }

我做错的主要事情是调用了两个ASPX页面(静态)的Web方法。我已经将从MSSQL表获取数据的方法移动到ASMX Web服务中,现在一切都正常工作。 - Anub1s
1个回答

2

这可以通过使用“处理器模式”来实现,在此模式中,当导入开始时建立一个“资源”(window.setInterval()),并在导入的承诺解决时进行处理。

外部变量的需求消失了,RunImportDeferred() 的需求也消失了。 RunImportAjax() 可以重命名为 RunImport() 或类似名称。 CheckActiveImports()OnCheckSuccess() 可以保持不变。

function RunImport() {
    $('#output2').html("Running import...");
    var intervalId = window.setInterval(CheckActiveImports, 5000); // establish a progress observer
    return $.ajax({
        url: 'exhaust.aspx/RunImport',
        async:true,
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        dataType: 'json'
    }).then(function (result) {
        var data = result.d,
            res;
        if (data.length) {
            res = jQuery.map(data, function(item) {
                return 'Job finished: ' + item.JobType + '. ' + item.Counter + ' records processed.';
            }).join('<br />');
        } else {
            res = '';
        }
        $('#output2').html(res);
        return result; // in case RunImport's caller is interested.
    }, function(error) {
        $('#output2').html(error.message);
    }).always(function() {
        window.clearInterval(intervalId); // dispose of the progress observer
    });
}

如果服务器有足够的容量来处理导入和进度请求,那么这应该可行。
进度观察器可以更好地编写,不涉及window.setInterval(),但这可能是一个单独的问题。
编辑:没有setInterval()的周期性观察器 没有setInterval()的周期性观察器相当简单,停止它比原来的clearInterval()更好,还可以(可选)中止任何未决的ajax请求。
在下面的方法中:
  • 通过传递几个参数来控制CheckActiveImports()(和OnCheckSuccess())的详细行为。
  • CheckActiveImports()返回一个停止函数,该函数在RunImport()中用作处理器。
在调用者中,您可以按以下方式调用返回的处理器函数:
  • dispose():停止进行进一步的ajax调用。
  • dispose(true):中止未决的ajax调用并停止进行进一步的ajax调用。
你可能想要后者。
function CheckActiveImports($container, interval, initMessage, finalMessage) {
    $container.html(initMessage || '');
    var jqXHR = null,
        timeoutRef = setTimeout(check, interval),
        _recurse_ = true;
    function check() {
        jqXHR = $.ajax({
            url: "exhaust.aspx/GetRunningImports",
            async: true,
            type: "GET",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: OnCheckSuccess.bind(null, $container),
            error: function (result) {
                $container.text("Error: " + result.responseText);
            },
            complete: function() {
                if(_recurse_) {
                    timeoutRef = setTimeout(check, interval); // recursive call
                }
            }
        });
    }
    return function(abort) {
        if(abort && jqXHR) jqXHR.abort();
        clearTimeout(timeoutRef);
        _recurse_ = false;
        $container.text(finalMessage || '');
    };
}

function OnCheckSuccess($container, result) {
    var data = result.d, //JSON.parse(result).d
        res;
    if(data.length) {
        res = jQuery.map(data, function(item) {
            return 'Job running: ' + item.JobType + '. ' + item.Counter + ' of ' + item.Total + ' records processed<br /> \
                Speed: ' + item.Speed + ' records per minute. \
                Estimated time of completion: ' + item.EstTime + '.';
        }).join('<br/>');
    } else {
        res = 'Currently there are no jobs running.';
    }
    $container.text(res);
}

function RunImport() {
    var $container = $('#output2').html("Running import...");
    var dispose = CheckActiveImports($('#output'), 5000, 'Checking for running imports...', ''); // start the progress observer
    return $.ajax({
        url: 'exhaust.aspx/RunImport',
        async:true,
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        dataType: 'json'
    }).then(function (result) {
        var data = result.d,
            res;
        if (data.length) {
            res = jQuery.map(data, function(item) {
                return 'Job finished: ' + item.JobType + '. ' + item.Counter + ' records processed.';
            }).join('<br />');
        } else {
            res = '';
        }
        $container.html(res);
        return result; // in case RunImport's caller is interested.
    }, function(error) {
        $container.html(error.message);
    }).always(function() {
        dispose(true); // dispose of the progress observer and abort any pending ajax.
    });
}

你的代码非常棒,非常感谢!我也不是window.setInterval()的粉丝,你能否给出更好的进度观察器实现的提示(或一些描述它的链接)? - Anub1s
哇,我以为我懂JavaScript和AJAX,但看起来我离你的水平远着呢。感谢你分享又一个很棒的代码片段! - Anub1s
很不幸,即使是我也会犯错。我刚刚做了几个更正。 - Roamer-1888
没关系,只有那些什么都不做的人才不会犯错误。我已经将_recurse_移动到CheckActiveImports参数中,以便在document.ready上调用它而不重复。 - Anub1s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接