Оптимизировать добавление в таблицу в DataTable

131
03 января 2020, 17:10

Имеется ajax запрос который получает строку json c данными .Сам запрос выполняется быстро но отображение в таблице происходит когда цикл пройдет все записи А записей может быть более 12000 тыс .Как можно пока цикл еще проходит уже отображать данные

$.ajax({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            },
            type:'POST',
            url:'{{route('payment')}}',
            data: {startdate: startdate,enddate:enddate,service:service,terminal:terminal,status:status},
            success: function (response) {

                payments=response.payments;
                table
                    .clear()
                    .draw();
                var content ="";
               for(i=0; i<payments.length; i++) {
                    table.row.add(
                        [
                            payments[i].id,
                            payments[i].created_at,
                            payments[i].terminal,
                            payments[i].sumforpay,
                            payments[i].commission,
                            payments[i].dst,
                            payments[i].service,
                            payments[i].status,
                            '<a style="color: black"  href="/printcheck/'+payments[i].id+'">Печать чека</a>',
                            Math.abs( payments[i].difference),
                        ]
                    ).draw();


                }
all_pskts=response.paymentsinfo[0].allcommission-response.paymentsinfo[0].sumpskts
                $('#all_pay').text("Всего платежей : "+response.paymentsinfo[0].count)
                $('#all_sum').text("На сумму : "+response.paymentsinfo[0].allsum)
                $('#all_com').text("Общая коммиссия : "+response.paymentsinfo[0].allcommission)
                $('#sumpskts').text("Сумма PSKTS : "+response.paymentsinfo[0].sumpskts)
                $('#all_sumpskts').text("Коммиссия : "+all_pskts)
                $(".lds-default").css("display", "none");
            },

        }); 
Answer 1

Вариант 1

Могу посоветовать вот такое решение, с использованием функции forEachAsync, которую я сделал на основании другого ответа в Stack Overflow.

Для оптимизации можно поиграть с параметром maxTimePerChunk. В моей функции если он меньше или равен 20, то он показывает количество лупов перед высвобождением потока. Если больше 20, то это время в миллисекундах исполнения цикла перед высвобождением потока.

// https://stackoverflow.com/a/10344560/9921853
// Loop over array in a non-blocking manner
var forEachAsync = function (array, fn, maxTimePerChunk, context) {
    return new Promise(function (resolve, reject) {
        context = context || window;
        maxTimePerChunk = maxTimePerChunk || 100;
        var index = 0;
        //Number <=20 is a number of iterations per chunk
        if (maxTimePerChunk > 20) {
            function now() {
                return new Date().getTime();
            }
            function doTimedChunk() {
                //console.log("Async chunk by time. Index = " + index);
                var startTime = now();
                while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
                    // callback called with args (value, index, array)
                    fn.call(context, array[index], index, array);
                    ++index;
                }
                if (index < array.length) {
                    // set Timeout for async iteration
                    setTimeout(doTimedChunk, 1);
                } else {
                    resolve();
                }
            }
            doTimedChunk();
        } else {
            var nIterPerChunk = maxTimePerChunk;
            function doCountChunk() {
                //console.log("Async chunk by count. Index = " + index);
                var cnt = nIterPerChunk;
                while (cnt-- && index < array.length) {
                    // callback called with args (value, index, array)
                    fn.call(context, array[index], index, array);
                    ++index;
                }
                if (index < array.length) {
                    // set Timeout for async iteration
                    setTimeout(doCountChunk, 1);
                } else {
                    resolve();
                }
            }
            doCountChunk();
        }
    });
}

$.ajax({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    },
    type: 'POST',
    url: '{{route('payment')}}',
    data: { startdate: startdate, enddate: enddate, service: service, terminal: terminal, status: status },
    success: async function (response) {
        payments = response.payments;
        table
            .clear()
            .draw();
        var content = "";
        await forEachAsync(payments, function (payment) {
            table.row.add(
                [
                    payment.id,
                    payment.created_at,
                    payment.terminal,
                    payment.sumforpay,
                    payment.commission,
                    payment.dst,
                    payment.service,
                    payment.status,
                    '<a style="color: black"  href="/printcheck/' + payment.id + '">Печать чека</a>',
                    Math.abs(payment.difference),
                ]
            ).draw();
        }, 10);
        all_pskts = response.paymentsinfo[0].allcommission - response.paymentsinfo[0].sumpskts
        $('#all_pay').text("Всего платежей : " + response.paymentsinfo[0].count)
        $('#all_sum').text("На сумму : " + response.paymentsinfo[0].allsum)
        $('#all_com').text("Общая коммиссия : " + response.paymentsinfo[0].allcommission)
        $('#sumpskts').text("Сумма PSKTS : " + response.paymentsinfo[0].sumpskts)
        $('#all_sumpskts').text("Коммиссия : " + all_pskts)
        $(".lds-default").css("display", "none");
    },
}); 

Вариант 2

Второй вариант несколько более упрощен в плане функционала и может быть даже медленнее, но решает важную проблему - в процессе заполнения таблицы Вы можете перемещаться по ней, используя Pagination.

Для этого пришлось применить "нечестный" прием с перехватом одной из функций отрисовки. Проблема в том, что Datatables отрисовывают панель Pagination при каждой команде draw, не зависимо от её параметров. При этом панель стирается и рисуется заново. Если изменения происходят быстро, то пользователь просто не успевает нажать на кнопки смены страницы - они исчезают раньше.

Решение - отрисовка панели Pagination только при изменениях текущей страницы или общего количества страниц. Можно было бы еще отслеживать изменения размера экрана, так как в этом случае тоже понадобится отрисовка по-новой. Но так как решение действует только пока добавляются данные, то этим можно пренебречь.

Есть ещё один недостаток - приходится вводить задержку, так как при слишком быстром заполнении данными - количество страниц тоже меняется быстро, и пользователь снова не успевает нажимать кнопки смены страницы...

Задержку можно подстраивать, либо можно вызывать draw не для каждой добавленной строки.

Исправленный код из вопроса - ниже. Смотрите также JSFiddle с упрощенным рабочим примером.

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
var oldPaginationCb;
var prevPage=-1, prevPages=-1;
var isCustomPaginationEnabled = false; //Our custom actions are disabled by default
function myPaginationCb ( settings ) {
  var
      start      = settings._iDisplayStart,
      len        = settings._iDisplayLength,
      visRecords = settings.fnRecordsDisplay(),
      all        = len === -1,
      page = all ? 0 : Math.ceil( start / len ),
      pages = all ? 1 : Math.ceil( visRecords / len );
  if (! (isCustomPaginationEnabled && page === prevPage && pages === prevPages)) {
      prevPage = page;
      prevPages = pages;
      return oldPaginationCb(settings);
  }
}

//Replacing the pagination draw callback function
var paginationCallback = table.fnSettings().aoDrawCallback.filter(cb => { return cb.sName === "pagination" })[0];
oldPaginationCb = paginationCallback.fn;
paginationCallback.fn = myPaginationCb;
$.ajax({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    },
    type: 'POST',
    url: '{{route('payment')}}',
    data: { startdate: startdate, enddate: enddate, service: service, terminal: terminal, status: status },
    success: async function (response) {
        payments = response.payments;
        table
            .clear()
            .draw();
        var content = "";
        isCustomPaginationEnabled = true;
        Array.prototype.forEach(payments, function (payment) {
            table.row.add(
                [
                    payment.id,
                    payment.created_at,
                    payment.terminal,
                    payment.sumforpay,
                    payment.commission,
                    payment.dst,
                    payment.service,
                    payment.status,
                    '<a style="color: black"  href="/printcheck/' + payment.id + '">Печать чека</a>',
                    Math.abs(payment.difference),
                ]
            ).draw(false);
            await sleep(50);
        });
        isCustomPaginationEnabled = false;
        all_pskts = response.paymentsinfo[0].allcommission - response.paymentsinfo[0].sumpskts
        $('#all_pay').text("Всего платежей : " + response.paymentsinfo[0].count)
        $('#all_sum').text("На сумму : " + response.paymentsinfo[0].allsum)
        $('#all_com').text("Общая коммиссия : " + response.paymentsinfo[0].allcommission)
        $('#sumpskts').text("Сумма PSKTS : " + response.paymentsinfo[0].sumpskts)
        $('#all_sumpskts').text("Коммиссия : " + all_pskts)
        $(".lds-default").css("display", "none");
    },
}); 
READ ALSO
Экспорт базы данных по дате laravel

Экспорт базы данных по дате laravel

Всем привет, не могу экспортировать базу по датеМой контроллер

141
Обновление SQL на сервере через PHP?

Обновление SQL на сервере через PHP?

Подскажите кодом в PHP, у меня есть база данных в sql на сервере, залита таблица с полями id, name и int и мне нужно чтоб скрипт проверял каждые 10 сектаблицу...

194
CentOS 6 несколько версий php

CentOS 6 несколько версий php

На сервере установлено ПО требующее php53

118
Почему не отрабатывает ajax?

Почему не отрабатывает ajax?

Таблица с данными из бд:

135