代码之家  ›  专栏  ›  技术社区  ›  Dennis

如何通过减少请求量来改进Ajax Live搜索

  •  3
  • Dennis  · 技术社区  · 6 年前

    我正在构建一个Ajax实时搜索页面。到目前为止,一切都按预期工作,但我注意到我正在进行大量的Ajax调用。

    我知道发生这种情况的地点和原因,但我找不到阻止这些Ajax调用发生的方法。

    我将尝试给出一个快速的解释,然后粘贴下面的代码。

    在页面的左侧,我有一组过滤器(例如位置、半径、兴趣、约定、日期范围等)。他们中的每一个人都有一个事件监听器(如改变)。当其中一个监听器被触发时,我更新查询参数并通过Ajax请求结果。

    现在,当有人选择了10个兴趣点(其中36个兴趣点),然后共享URL时,就会出现问题,如下所示:

    http://localhost/codeigniter/nl-be/sociale-teambuildings/zoeken?locations=&distance=0&minemployees=0&maxemployees=1000&minprice=0&maxprice=50000&interests=1%3B2%3B3%3B4%3B5%3B6%3B7%3B8%3B9%3B10&sdgs=&startdate=2018-12-03&enddate=2019-12-03&engagements=

    首先,我会说我正在使用icheck.js作为我的复选框。这意味着对“ifchecked”事件有一个检查。 由于列表包含10个感兴趣的条目,因此“ifchecked”事件将被激发10次,从而产生10个Ajax请求。现在,结合5个SDG、2个Engagagement、3个地点考虑这一点……最后我得到了大量的Ajax请求。 同时删除我所有的兴趣(有一个“删除”链接),它将触发“ifunchecked”事件10次,从而再次执行10个Ajax请求。

    这是我完整的JS代码,我尝试用HTML和所有东西创建一个JS,但是代码有点与CodeIgniter框架混合在一起,很难放在里面。但是JS代码足以让图片显示出来。

    //Set vars available to entire scope for filtering
    var searchLocation = null;
    var searchRadius = 0;
    var searchMin = 0;
    var searchMax = 1000;
    var searchMinPrice = 0;
    var searchMaxPrice = 50000;
    var searchSelectedInterests = [];
    var searchSelectedSdgs = [];
    var searchStartDate = moment().format('YYYY-MM-DD');
    var searchEndDate = moment().add(1, 'years').format('YYYY-MM-DD');
    var searchSelectedEngagements = [];
    
    var getUrl = window.location;
    var baseUrl = getUrl .protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
    
    $(document).ready(function(){
        'use strict';
    
        // Square Checkbox & Radio
        $('.skin-square input').iCheck({
            checkboxClass: 'icheckbox_square-blue'
        });
        $('.searchinterests input').on('ifChecked', function(event) {
            var interestid = event.target.value;
            searchSelectedInterests.push(interestid);
            updateURLParameters();
            performSearch();
        });
        $('.searchinterests input').on('ifUnchecked', function(event) {
            var interestid = event.target.value;
            var arrayPos = $.inArray(interestid, searchSelectedInterests);
            if (arrayPos > -1) {
                searchSelectedInterests.splice(arrayPos, 1);
            }
            performSearch();
        });
        $('.searchsdgs input').on('ifChecked', function(event) {
            var sdgid = event.target.value;
            searchSelectedSdgs.push(sdgid);
            updateURLParameters();
            performSearch();
        });
        $('.searchsdgs input').on('ifUnchecked', function(event) {
            var sdgid = event.target.value;
            var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
            if (arrayPos > -1) {
                searchSelectedSdgs.splice(arrayPos, 1);
            }
            performSearch();
        });
        $('.searchengagements input').on('ifChecked', function(event) {
            var social_engagement_id = event.target.value;
            searchSelectedEngagements.push(social_engagement_id);
            updateURLParameters();
            performSearch();
        });
        $('.searchengagements input').on('ifUnchecked', function(event) {
            var social_engagement_id = event.target.value;
            var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
            if (arrayPos > -1) {
                searchSelectedEngagements.splice(arrayPos, 1);
            }
            performSearch();
        });
    
        var searchLocationSelect = $('.stb-search-location');
        var radiusSlider = document.getElementById('radius-slider');
        var minMaxSlider = document.getElementById('min-max-slider');
        var priceSlider = document.getElementById('price-slider');
        var daterangepicker = $('#searchdaterange');
        var curr_lang = $('#curr_lang').val();
    
        switch(curr_lang) {
            case 'nl':
                moment.locale('nl');
                daterangepicker.daterangepicker({
                    minDate: moment(),
                    startDate:  moment(),
                    endDate: moment().add(1, 'years'),
                    ranges: {
                        'Volgende week': [moment(), moment().add(1, 'weeks')],
                        'Volgende maand': [moment(), moment().add(1, 'months')],
                        'Volgende 3 maanden': [moment(), moment().add(3, 'months')],
                        'Volgende 6 maanden': [moment(), moment().add(6, 'months')],
                        'Volgend jaar': [moment(), moment().add(1, 'years')]
                    },
                    alwaysShowCalendars: true,
                    locale: {
                        "customRangeLabel": "Vrije keuze",
                        "format": "DD-MM-YYYY"
                    }
                });
                break;
            case 'en':
                moment.locale('en');
                daterangepicker.daterangepicker({
                    minDate: moment(),
                    startDate:  moment(),
                    endDate: moment().add(1, 'years'),
                    ranges: {
                        'Next week': [moment(), moment().add(1, 'weeks')],
                        'Next month': [moment(), moment().add(1, 'months')],
                        'Next 3 months': [moment(), moment().add(3, 'months')],
                        'Next 6 months': [moment(), moment().add(6, 'months')],
                        'Next year': [moment(), moment().add(1, 'years')]
                    },
                    alwaysShowCalendars: true,
                    locale: {
                        "customRangeLabel": "Free choice",
                        "format": "DD-MM-YYYY"
                    }
                });
                break;
            case 'fr':
                moment.locale('fr');
                daterangepicker.daterangepicker({
                    minDate: moment(),
                    startDate:  moment(),
                    endDate: moment().add(1, 'years'),
                    ranges: {
                        'Semaine prochaine': [moment(), moment().add(1, 'weeks')],
                        'Mois prochain': [moment(), moment().add(1, 'months')],
                        '3 mois prochains': [moment(), moment().add(3, 'months')],
                        '6 mois prochains': [moment(), moment().add(6, 'months')],
                        'L\'année prochaine': [moment(), moment().add(1, 'years')]
                    },
                    alwaysShowCalendars: true,
                    locale: {
                        "customRangeLabel": "Libre choix",
                        "format": "DD-MM-YYYY"
                    }
                });
                break;
        }
        daterangepicker.on('hide.daterangepicker', function (ev, picker) {
            var startdate = picker.startDate.format('YYYY-MM-DD');
            var enddate = picker.endDate.format('YYYY-MM-DD');
            setStartDate(startdate);
            setEndDate(enddate);
            updateURLParameters();
            performSearch();
        });
    
        if (searchLocationSelect.length) {
            searchLocationSelect.selectize({
                create: false,
                sortField: {
                    field: 'text',
                    direction: 'asc'
                },
                dropdownParent: 'body',
                plugins: ['remove_button'],
                onChange: function(value) {
                    setLocation(value);
                    var size = value.length;
                    if (size == 1) {
                        enableRadius(radiusSlider);
                    } else {
                        disableAndResetRadius(radiusSlider);
                    }
                    updateURLParameters();
                    performSearch();
                }
            });
        }
    
        noUiSlider.create(radiusSlider, {
            start: [0],
            step: 5,
            range: {
                'min': 0,
                'max': 100
            }
        });
        var radiusNodes = [
            document.getElementById('radius-value')
        ];
        // Display the slider value and how far the handle moved
        // from the left edge of the slider.
        radiusSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
            var value = values[handle];
            radiusNodes[handle].innerHTML = Math.round(value);
        });
        radiusSlider.noUiSlider.on('set', function (value) {
            setRadius(value);
            updateURLParameters();
            performSearch();
        });
    
        var minmax_slider_options = {
            start: [0,1000],
            behaviour: 'drag',
            connect: true,
            tooltips: [wNumb({
                decimals: 0
            }), wNumb({
                decimals: 0
            })],
            range: {
                'min': 0,
                'max': 1000
            },
            step: 5
        };
        noUiSlider.create(minMaxSlider, minmax_slider_options);
        var minMaxNodes = [
            document.getElementById('minmax-lower-value'),
            document.getElementById('minmax-upper-value')
        ];
        // Display the slider value and how far the handle moved
        // from the left edge of the slider.
        minMaxSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
            var value = values[handle];
            minMaxNodes[handle].innerHTML = Math.round(value);
        });
        minMaxSlider.noUiSlider.on('set', function (values) {
            setMin(values[0]);
            setMax(values[1]);
            updateURLParameters();
            performSearch();
        });
    
        var price_slider_options = {
            start: [0,50000],
            behaviour: 'drag',
            connect: true,
            tooltips: [wNumb({
                decimals: 0
            }), wNumb({
                decimals: 0
            })],
            range: {
                'min': 0,
                'max': 50000
            },
            step: 250
        };
        noUiSlider.create(priceSlider, price_slider_options);
        var priceNodes = [
            document.getElementById('price-lower-value'), // 1000
            document.getElementById('price-upper-value')  // 3500
        ];
        // Display the slider value and how far the handle moved
        // from the left edge of the slider.
        priceSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
            var value = values[handle];
            priceNodes[handle].innerHTML = '€'+Math.round(value);
        });
        priceSlider.noUiSlider.on('set', function (values) {
            setMinPrice(values[0]);
            setMaxPrice(values[1]);
            updateURLParameters();
            performSearch();
        });
    
        /** Reset methods **/
        $('#resetLocations').on('click', function(e) {
            e.preventDefault();
            var locationInputField = $('.stb-search-location');
            var control = locationInputField[0].selectize;
            control.clear();
        });
    
        $('#resetRadius').on('click', function(e) {
            e.preventDefault();
            document.getElementById('radius-slider').noUiSlider.set(0);
        });
    
        $('#resetMinMax').on('click', function(e) {
            e.preventDefault();
            document.getElementById('min-max-slider').noUiSlider.set([0,1000]);
        });
    
        $('#resetPrice').on('click', function(e) {
            e.preventDefault();
            document.getElementById('price-slider').noUiSlider.set([0,50000]);
        });
    
        $('#resetInterests').on('click', function(e) {
            e.preventDefault();
            searchSelectedInterests = [];
            $("input[name='interests[]']").iCheck('uncheck');
        });
    
        $('#resetSdgs').on('click', function(e) {
            e.preventDefault();
            searchSelectedSdgs = [];
            $("input[name='sdgs[]']").iCheck('uncheck');
        });
    
        $('#resetDate').on('click', function(e) {
            e.preventDefault();
            $('#searchdaterange').data('daterangepicker').setStartDate(moment());
            $('#searchdaterange').data('daterangepicker').setEndDate(moment().add(1, 'years'));
    
            var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
            var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
            setStartDate(startdate);
            setEndDate(enddate);
            performSearch();
        });
    
        $('#resetEngagement').on('click', function(e) {
            e.preventDefault();
            searchSelectedEngagements = [];
            $("input[name='engagement[]']").iCheck('uncheck');
        });
    
        // Set initial parameters (and pre-fill the filters based on query params) 
        setupConfig(radiusSlider);
    });
    
    
    function getQueryStringValue(){
        var currentURL = new URI();
    
        var queryParams = currentURL.query(true);
        if ($.isEmptyObject(queryParams) === false) {
            return queryParams;
        } else {
            return undefined;
        }
    }
    
    //In here we read the query parameters from the URL and set the filters to the query parameters (+ do initial filtering)
    function setupConfig(radiusSlider) {
        var queryParams = getQueryStringValue();
    
        if (queryParams !== undefined) {
            var locations = queryParams.locations;
            if (locations !== undefined) {
                var locationsArray = locations.split(";");
                fillLocations(locationsArray);
                if (locationsArray.length != 1) {
                    disableAndResetRadius(radiusSlider);
                }
            } else {
                disableAndResetRadius(radiusSlider);
            }
    
            var distance = queryParams.distance;
            if (distance !== undefined) {
                if (locationsArray.length != 1) {
                    disableAndResetRadius(radiusSlider);
                } else {
                    document.getElementById('radius-slider').noUiSlider.set(distance);
                }
            }
    
            var minEmployees = queryParams.minemployees;
            var maxEmployees = queryParams.maxemployees;
            if ((minEmployees !== undefined) && (maxEmployees !== undefined)) {
                document.getElementById('min-max-slider').noUiSlider.set([minEmployees,maxEmployees]);
            }
    
            var minPrice = queryParams.minprice;
            var maxPrice = queryParams.maxprice;
            if ((minPrice !== undefined) && (maxPrice !== undefined)) {
                document.getElementById('price-slider').noUiSlider.set([minPrice,maxPrice]);
            }
    
            var interests = queryParams.interests;
            if (interests !== undefined) {
                var interestsArray = interests.split(";");
                fillInterests(interestsArray);
            }
    
            var sdgs = queryParams.sdgs;
            if (sdgs !== undefined) {
                var sdgsArray = sdgs.split(";");
                fillSdgs(sdgsArray);
            }
    
            var startdate = queryParams.startdate;
            var enddate = queryParams.enddate;
            if ((startdate !== undefined) && (enddate !== undefined)) {
                $('#searchdaterange').data('daterangepicker').setStartDate(moment(startdate));
                $('#searchdaterange').data('daterangepicker').setEndDate(moment(enddate));
    
                var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
                var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
                setStartDate(startdate);
                setEndDate(enddate);
            }
    
            var engagements = queryParams.engagements;
            if (engagements !== undefined) {
                var engagementsArray = engagements.split(";");
                fillEngagements(engagementsArray);
            }
        } else {
            disableAndResetRadius(radiusSlider);
            performSearch();
        }
    }
    
    function fillLocations(locations) {
        var selectize = $('.stb-search-location');
        selectize[0].selectize.setValue(locations);
    }
    
    function fillInterests(interests) {
        for (var i = 0; i < interests.length; i++) {
            var checkboxId = "interest-"+interests[i];
            var checkbox = $('#'+checkboxId);
            checkbox.iCheck('check');
        }
    }
    
    function fillSdgs(sdgs) {
        for (var i = 0; i < sdgs.length; i++) {
            var checkboxId = "sdg-"+sdgs[i];
            var checkbox = $('#'+checkboxId);
            checkbox.iCheck('check');
        }
    }
    
    function fillEngagements(engagements) {
        for (var i = 0; i < engagements.length; i++) {
            var checkboxId = "eng-"+engagements[i];
            var checkbox = $('#'+checkboxId);
            checkbox.iCheck('check');
        }
    }
    
    function getCurrLang() {
        return $('#curr_lang').val();
    }
    
    function getCurrLangSegment() {
        return $('#curr_abbr').val();
    }
    
    function getLocation() {
        return $('#location').val();
    }
    
    function setLocation(value) {
        searchLocation = value;
    }
    
    function setRadius(value) {
        searchRadius = value;
    }
    
    function disableAndResetRadius(radiusSlider) {
        radiusSlider.noUiSlider.set(0);
        radiusSlider.setAttribute('disabled', true);
    }
    
    function enableRadius(radiusSlider) {
        radiusSlider.removeAttribute('disabled');
    }
    
    function setMin(value) {
        searchMin = value;
    }
    
    function setMax(value) {
        searchMax = value;
    }
    
    function setMinPrice(value) {
        searchMinPrice = value;
    }
    
    function setMaxPrice(value) {
        searchMaxPrice = value;
    }
    
    function setStartDate(value) {
        searchStartDate = value;
    }
    
    function setEndDate(value) {
        searchEndDate = value;
    }
    
    function performSearch() {
        $('#stb-items-placeholder').html('<div id="loading"><span>'+res.StbSearchPlaceholder+'</span></div>');
    
        var searchOptions = {
            type: 'POST',
            url: baseUrl + '/dashboard/socialteambuildings/search/getFilteredStbs',
            dataType: 'json'
        };
    
        var filterdata = {
            "curr_lang" : getCurrLang(),
            "curr_abbr" : getCurrLangSegment(),
            "location" : getLocation(),
            "interests": searchSelectedInterests,
            "sdgs": searchSelectedSdgs
        };
    
        var options = $.extend({}, searchOptions, {
            data: filterdata
        });
    
        // ajax done & fail
        $.ajax(options).done(function (data) {
            var mustacheJsonData = data.result;
    
            var html = Mustache.render( $('#stbItemGen').html(), mustacheJsonData );
            $('#stb-items-placeholder').html( html );
        });
    }
    
    function updateURLParameters() {
        if (searchLocation != null) {
            var locationsUrlString = searchLocation.join(";");
        }
    
        var distanceUrlString = Math.round(searchRadius[0]);
        var minEmployeesUrlString = Math.round(searchMin);
        var maxEmployeesUrlString = Math.round(searchMax);
        var minPriceUrlString = Math.round(searchMinPrice);
        var maxPriceUrlString = Math.round(searchMaxPrice);
        var interestUrlString = searchSelectedInterests.join(";");
        var sdgUrlString = searchSelectedSdgs.join(";");
        var startDateUrlString = searchStartDate;
        var endDateUrlString = searchEndDate;
        var engagementUrlString = searchSelectedEngagements.join(";");
    
        var params = {locations: locationsUrlString, distance: distanceUrlString, minemployees: minEmployeesUrlString, maxemployees: maxEmployeesUrlString, minprice: minPriceUrlString, maxprice: maxPriceUrlString, interests: interestUrlString, sdgs: sdgUrlString, startdate: startDateUrlString, enddate: endDateUrlString, engagements: engagementUrlString};
        var query = $.param(params);
        addURLParameter(query);
    }
    
    //This function removes all the query parameters and adds the completely newly built query param string again
    function addURLParameter(queryString){
        var currentUrl = window.location.href;
        var urlNoQueryParams = currentUrl.split("?");
        var baseUrl = urlNoQueryParams[0];
        var result = baseUrl + "?" + queryString;
        window.history.replaceState('', '', result);
    }
    

    例如,我尝试在remove选项上使用e.stoppropagation()和e.stopImmediatePropagation()。这并不能阻止返回到ICheck库的事件。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Nikos M.    6 年前

    Debounce在这里不起作用,因为问题不在于一个事件监听器在短时间内有太多的请求,而在于许多独立的事件监听器一个接一个地触发请求。

    可能的解决方案:

    1. 添加按钮EG SEARCH 它将实际执行搜索,而不是由单个更新触发。对于许多独立的听众来说,这是一个很好且简单的解决方案。

    2. 如果你不想添加一个新的按钮,做如下的事情。添加时间间隔 setInterval 使用Ajax执行搜索。并标记是否应执行搜索。然后,当复选框上的任一侦听器更改时,只需将标志设置为 true .另外,如果一个请求已经在进行中,那么在当前的Ajax请求完成之前不要再发出另一个Ajax请求。

    伪代码如下:

    var do_search = false, timer = null, doing_ajax = false, TROTTLE = 500;
    timer = setTimeout(performSearch, TROTTLE);
    
    function performSearch()
    {
        if ( !do_search || doing_ajax )
        {
            timer = setTimeout(performSearch, TROTTLE);
            return;
        }
         doing_ajax = true;
         do_search = false;
         // do the ajax request here
         // ...
         // NOTE: on ajax complete reset the timer, eg timer = setTimeout(performSearch, TROTTLE);
         // and set doing_ajax = false;
    }
    
    // then your checkboxes listeners will simply update the do-search flag eg:
        $('.searchsdgs input').on('ifChecked', function(event) {
            var sdgid = event.target.value;
            searchSelectedSdgs.push(sdgid);
            updateURLParameters();
            //performSearch();
            do_search = true;
        });
        $('.searchsdgs input').on('ifUnchecked', function(event) {
            var sdgid = event.target.value;
            var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
            if (arrayPos > -1) {
                searchSelectedSdgs.splice(arrayPos, 1);
            }
            //performSearch();
            do_search = true;
        });
        $('.searchengagements input').on('ifChecked', function(event) {
            var social_engagement_id = event.target.value;
            searchSelectedEngagements.push(social_engagement_id);
            updateURLParameters();
            //performSearch();
            do_search = true;
        });
        $('.searchengagements input').on('ifUnchecked', function(event) {
            var social_engagement_id = event.target.value;
            var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
            if (arrayPos > -1) {
                searchSelectedEngagements.splice(arrayPos, 1);
            }
            //performSearch();
            do_search = true;
        });
    

    注释 你可以调整 TROTTLE 在更直接的交互性和更少的Ajax请求之间进行平衡的时间间隔。尝试不同的值,以获得对你的应用程序的感觉。

    备注2 您可以在此示例的基础上构建,使其像 多次去抖动 例如,通过清除超时并在每个侦听器中重置它(而不是简单地设置 do_search=true 你可以设定 DoSyx=真 清除上一个间隔,然后重新设置)。这将确保只执行最后一次更新。