Параллельные запросы AJAX в jQuery

Мое приложение запускает до 180 заданий AJAX, которые интенсивно работают на стороне сервера (длинные запросы SELECT).

Я хотел бы оптимизировать нагрузку на несколько ядер процессора, которые у меня есть, переключение с дизайна, в котором каждый вызов AJAX выполняется последовательно для дизайна, в котором эти запросы выполняются с максимальным, скажем, 4 параллельно.

Возможно, но уродливое решение может одновременно выдавать все 180 запросов на клиенте и использовать сервер Semaphore на уровне Session или Application. Я расскажу о загрузке приложений позже.

Я хотел бы найти более приятное решение, в котором все вызовы начинаются по порядку (каждая строка в таблице представляет собой другой запрос проверки), но когда кто-то завершает работу, начинается следующее, и число (а именно, 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 в моем сценарии?

[edit] забыл обсудить требования приложений: это приложение работает вживую, но не обслуживает большую пользовательскую базу. Когда пользователь отправляет данные, которые необходимо проверить, приложение будет выполнять интенсивную работу, оставаясь бездействующим в течение длительных периодов времени.

Ответ 1

$.ajax() - это асинхронная функция, которая инициирует запрос и затем немедленно возвращается. Поэтому, если вы вызываете его несколько раз подряд, он будет создавать параллельные запросы.

Ваш код по-прежнему работает в одном потоке, но HTTP-запросы выполняются параллельно в фоновом режиме, а jQuery вызывает ваши обратные вызовы по мере их возврата. Таким образом, вы получаете parallelism без непосредственного взаимодействия с потоками.

В вашем GlobalCheck():

var CONCURRENT_REQUESTS = 4;

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

запустит четыре параллельных запроса, и ваш существующий код вызовет новый запрос каждый раз, когда закончится, так что вы всегда будете иметь 4 параллельных запроса. И поскольку ваш код работает только в одном потоке, вам не нужно беспокоиться о проблемах concurrency с вашей переменной done и т.д.

Для получения более параллельных запросов просто увеличьте значение CONCURRENT_REQUESTS, но обратите внимание, что очень быстро вы нажмете ограничение браузера на одновременные запросы к одному домену - оно зависит от браузера, но оно всегда довольно небольшое. См. этот ответ для специфики.

Ответ 2

Если вы, например, имеете вызов ajax в функции "doCall", просто запустите эту функцию для того, чтобы x-сумма зависела от "потоков", которые вы хотите.

doCall(x);
doCall(x);
doCall(x);

Теперь у вас есть 3 потока. Чтобы сохранить это, перезапустите функцию в функции. Итак, в doCall Function у вас есть еще один doCall (x), чтобы "сохранить поток в живых".

У вас будет какой-то "цикл", и запросы будут продолжать получать асинхронный вызов.

Ответ 3

По-моему, вам нужен массив "nbRequest" , чтобы узнать, сколько запросов вы имеете. (4 макс.)

Затем используйте setInterval.

Запустите интервал, сохраните "num" внутри "nbRequest" . Когда запрос ajax будет завершен, удалите "num" из "nbRequest" .

В среднем время интервал "проверяет, равна ли длина" nbRequest "4. Если нет, вы начинаете новый запрос.

Когда "done == num", остановите интервал.

Ответ 4

Примечание. Количество одновременных запросов, которые браузеры могут сделать для данного домена, ограничен. Таким образом, вы можете одновременно запускать все свои запросы, браузер будет заботиться о их очередности и одновременно выполнять от 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();

Я оставлю вам обработку ошибок.

И если вам нужно будет добавить логику, если вы хотите определить, когда все запросы будут выполнены. Возможно, загляните в promises.

Ответ 5

У меня были трудности с этим, потому что я также пытался отслеживать ошибки и передавать сериализованный объект на серверную часть.

Следующее сработало для меня, надеюсь, это поможет

    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
        }