jQuery中的并行AJAX请求

11

我的应用程序在服务器端运行高达180个AJAX作业,这些作业是IO密集型的(长时间运行的SELECT查询)。

我想优化我有的多个CPU核心的负载,从一个顺序执行每个AJAX调用的设计切换到一个设计,在这个设计中,这些请求最多可以并行执行4个。

一个可能的但不美观的解决方案可能是在客户端同时发出所有180个请求,并让服务器使用存储在SessionApplication级别的Semaphore。稍后我将讨论应用程序工作量。

我想找到一种更好的解决方案,在这个方案中,所有调用都按顺序启动(表上的每一行是一个不同的检查查询),但是当任何一个调用终止时,下一个调用就会开始,并且会有一定数量(即4个)的并发AJAX请求及其对应的装载器指示器。

我尝试使用Threadpool-js,但我发现我不能从工作者中使用jQuery。

我当前的代码如下:

function GlobalCheck() { //entry point
        if (ValidateDate()) {
            //Below are global variables
            list = $(".chkClass:checked"); //Only checked rows deal to AJAX request
            num = $(".chkClass:checked").length; //Total number of ajax calls
            done = 0; //Count of complete calls. When it reaches num we are done!

            if (list.length == 0) {
                alert('...');
                return;
            }

            $(".pMessage").fadeOut();
            $(".tbStatus").html('');
            $(".submit").hide();
            $(".exportFunctions").fadeOut();
            $(".loader").show();
            $(":checkbox").attr('disabled', true);

            SingleCheck(0); //simplification, I do other non interesting things here
        }
    }


function SingleCheck(index) {
        aValue = $($(list).get(index)).val();

        var splitted = aValue.split('_');
        $('#loader_' + aValue).show();
        $('#green_' + aValue).hide();
        $('#yellow_' + aValue).hide();
        $('#red_' + aValue).hide();
        $('#print_' + aValue).hide();
        $('#xls_' + aValue).hide();
        $('#summ_' + aValue).hide();

        $.ajax({
            type: 'GET',
            url: '@Url.Action("Single", "Check")',
            data: {
                pType: splitted[0],
                pIdQuery: splitted[1],
                pDateBegin: $('#date_begin').attr('value'),
                pDateEnd: $('#date_end').attr('value'),
                pNow: Math.floor(Math.random() * 1000000)
            },
            success: function (data) {
                if (!CheckSessionExpired(data)) {
                    //alert(data);

                    $("#tdStatus_" + aValue).html(data);
                    $("#loader_" + aValue).hide();
                    done++; //Done 1 more query
                    $(".progress").each(function (i, cRow) { $(this).html([update status]); });

                    if (done == num) {    // Finish?
                        FinishCheck();
                    }
                    else {
                        SingleCheck(done); //Go to the next
                    }
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                alert(errorThrown);
                RedirectToError();
            }
        });
    }

结果如下:

Auitool

问题是:在我的情况下,我应该采取什么方法来创建并发的AJAX请求?

[编辑] 忘了讨论应用需求:该应用程序正在运行,但不为大量用户提供服务。当用户提交要检查的数据时,应用程序将执行密集操作,同时闲置很长时间。

5个回答

3

$.ajax() 是一种异步函数,它会立即发起请求并返回。因此,如果您连续多次调用它,它将创建并发请求。

您的代码仍在单个线程上运行,但HTTP请求在后台并行发生,jQuery在收到数据后调用您的回调函数。因此,您可以在不直接处理线程的情况下获得并行性。

在您的 GlobalCheck() 中:

var CONCURRENT_REQUESTS = 4;

while (done < CONCURRENT_REQUESTS) {
  SingleCheck(done++);
}

将会启动四个并行请求,您现有的代码会在每次一个请求完成时触发一个新请求,因此您总是会有4个并行请求在运行。并且,由于您的代码仅在单个线程上运行,所以您不必担心done变量等与并发相关的问题。

如果需要更多并发请求,只需增加CONCURRENT_REQUESTS的值即可,但请注意,很快您将会达到浏览器对单个域名并发请求的限制 - 这个数字因浏览器而异,但通常都是非常小的数字。参见这个答案获取具体信息


1
当你在一个名为“doCall”的函数中有一个ajax调用时,只需根据你想要的“线程”数量启动此函数x次。
doCall(x);
doCall(x);
doCall(x);

现在你有3个线程。为了让它继续运行,在函数内部重新启动函数。因此,在doCall函数中,您有另一个doCall(x)来“保持线程活动”。您将拥有某种“循环”,并且请求将继续异步触发。

0
在我看来,你需要一个数组“nbRequest”来知道有多少个请求正在工作(最多4个)。
然后使用setInterval。
启动间隔,将你的“num”存储在“nbRequest”中。 当Ajax请求完成时,从“nbRequest”中删除你的“num”。
同时,间隔会检查“nbRequest”的长度是否等于4。 如果不是,你就开始一个新的请求。
当“done == num”时,停止间隔。

0

注意:浏览器可以向给定域名发起的并发请求数量是有限制的。因此,你最好一次发送所有请求,浏览器会负责排队并仅执行 6 到 8 个并发请求。

来源:浏览器中的最大并行 HTTP 连接是多少?

但是,如果你想要更精确的控制,可以实现类似以下代码:

//define your list of URLs and what to do with the data
var sources = [
    {
        url : "http://...",
        callback : function(data){ /* do something */}  
    },
    {
        url : "http://...",
        callback : function(data){ /* do something */}  
    }
];

//state
var maxConcurrentRequests = 4;
var concurrentRequests = 0;
var currentSourceIndex = -1;

//this function wil ensure that as long as there are sources left, there are 4 requests running 
function startRequestIfNeeded(){

    while(currentSourceIndex < sources.length-1 && concurrentRequests < maxConcurrentRequests){
        currentSourceIndex++;
        concurrentRequests++;
        var source = sources[sourceIndex];
        doRequest(source);
    }
}

//this fires the request and executes the callback
function doRequest(source){

    $.getJSON(source.url, function(data){
        source.callback(data);
        concurrentRequests--;
        startRequestIfNeeded();
    });
}

startRequestIfNeeded();

我会把错误处理留给你。

如果你想要检测所有请求何时完成,你可能需要添加逻辑。也许可以研究一下 Promise。


谢谢,我已经相应更新了我的答案,并链接到了更为最新的问题。 - Pierre Henry

0

我遇到了困难,因为我还试图跟踪错误并将序列化对象传递到服务器端。

以下方法对我有用,希望能帮到你。

    var args1 = {
        "table": "users",
        "order": " ORDER BY id DESC ",
        "local_domain":""
    }
    var args2 = {
        "table": "parts",
        "order": " ORDER BY date DESC ",
        "local_domain":""
    }

    $.when(
        $.ajax({
                url: args1.local_domain + '/my/restful',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                type: "POST",
                dataType : "json",
                contentType: "application/json; charset=utf-8",
                data : JSON.stringify(args1),
                error: function(err1) {
                    alert('(Call 1)An error just happened...' + JSON.stringify(err1));
                }
            }),
        $.ajax({
            url: args2.local_domain + '/my/restful',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            type: "POST",
            dataType : "json",
            contentType: "application/json; charset=utf-8",
            data : JSON.stringify(args2),
            error: function(err2) {
                calert('(Call 2)An error just happened...' + JSON.stringify(err2));
            }
        })                     

    ).then(function( data1, data2 ) {
        data1 = cleanDataString(data1);
        data2 = cleanDataString(data2);

        data1.forEach(function(e){
            console.log("ids" + e.id)
        });
        data2.forEach(function(e){
            console.log("dates" + e.date)
        });

    })

    function cleanDataString(data){  // this is extra 
            data = decodeURIComponent(data);
            // next if statement was only used because I got additional object on the back of my JSON object
            // parsed it out while serialised and then added back closing 2 brackets
            if(data !== undefined && data.toString().includes('}],success,')){ 
                temp = data.toString().split('}],success,');
                data = temp[0] + '}]';
            }
            data = JSON.parse(data);
            return data;                    // return parsed object
        }

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