Как реагировать на событие Javascript, только если оно запускается один раз, а затем не запускается снова в течение некоторого периода времени? - PullRequest
11 голосов
/ 16 июля 2010

В моем приложении я слушаю событие Google Maps API 'bounds_changed', чтобы отправить ajax-запрос на обновление некоторого div на веб-странице в зависимости от новых границ карты:

google.maps.event.addListener(map, 'bounds_changed', function() {
  // here goes an ajax call
}

Событие 'bounds_changed' вызывается с высокой частотой, когда пользователь перетаскивает карту. Настолько, что на сервер отправлено слишком много запросов AJAX.

В основном я хотел бы сделать вызов ajax только после того, как пользователь перестал перемещать карту в течение некоторого периода времени (например, 500 мс). Я не очень опытен в Javascript и пытался добиться этого с помощью setTimeout и clearTimeout, но безуспешно.

Любая идея будет оценена:)

Ответы [ 6 ]

12 голосов
/ 16 июля 2010

Добавьте тайм-аут, который запускает ваш код через 500 мсек после срабатывания события, каждый раз при возникновении события сбрасывает тайм-аут и создает новый.

например.

google.maps.event.addListener(map, 'bounds_changed', (function () {
    var timer;
    return function() {
        clearTimeout(timer);
        timer = setTimeout(function() {
            // here goes an ajax call
        }, 500);
    }
}()));
4 голосов
/ 25 мая 2011

Google предлагает использовать другого слушателя ...

google.maps.event.addListener(map, 'idle', showMarkers);

цитата «Обратите внимание, что вы можете прослушивать событие bounds_changed, но оно срабатывает непрерывно, когда пользователь выполняет панорамирование; вместо этого простоя будет срабатывать, как только пользователь перестанет панорамировать / масштабировать». / Цитата

см

http://code.google.com/apis/maps/articles/toomanymarkers.html#gridbasedclustering

4 голосов
/ 16 июля 2010

Существует действительно хороший подход, доступный на unscriptable.com :

Function.prototype.debounce = function (threshold, execAsap) {
    var func = this, // reference to original function
        timeout; // handle to setTimeout async task (detection period)
    // return the new debounced function which executes the original function 
    // only once until the detection period expires
    return function debounced () {
        var obj = this, // reference to original context object
            args = arguments; // arguments at execution time
        // this is the detection function. it will be executed if/when the 
        // threshold expires
        function delayed () {
            // if we're executing at the end of the detection period
            if (!execAsap)
                func.apply(obj, args); // execute now
            // clear timeout handle
            timeout = null;
        };
        // stop any current detection period
        if (timeout)
            clearTimeout(timeout);
        // otherwise, if we're not already waiting and we're executing at the 
        // beginning of the waiting period
        else if (execAsap)
            func.apply(obj, args); // execute now
        // reset the waiting period
        timeout = setTimeout(delayed, threshold || 100);
    };
}

Это позволит вам сделать:

// call the function 200ms after the bounds_changed event last fired:
google.maps.event.addListener(map, 'bounds_changed', (function() {
  // here goes an ajax call
}).debounce(200));

// call the function only once per 200ms:
google.maps.event.addListener(map, 'bounds_changed', (function() {
  // here goes an ajax call
}).debounce(200,true));

Если вы не хотитедополнить Function.prototype автономную function debounce(func, threshold, execAsap) доступную в блоге .

2 голосов
/ 16 июля 2010

Это код Брентона Алкера, но перенесенный в служебную функцию.

var frequencyReduce = function(delay, callback){
    var timer;
    return function(){
        clearTimeout(timer);
        timer = setTimeout(callback, delay);
    };
};

google.maps.event.addListener(map, 'bounds_changed', frequencyReduce(500, function(){
    // here goes an ajax call
}));
2 голосов
/ 16 июля 2010

Этот код гарантирует, что прошло полсекунды с момента последнего запуска события, прежде чем делать свое дело (комментарий TODO). Я думаю, что это то, что вы хотите.

var mapMoveTimer;
google.maps.event.addListener(map, 'bounds_changed', function(){
  clearTimeout(mapMoveTimer);
  mapMoveTimer = setTimeout(function(){
    // TODO: stuff with map
  }, 500); 
});
0 голосов
/ 16 июля 2010

Одним из быстрых и грязных решений было бы менее частое обращение к серверу:

var requestCounter = 0;
var frequency = 50;

google.maps.event.addListener(map, 'bounds_changed', function() {
    if((++requestCounter % frequency) == 0){
            // here goes an ajax call (also, optionally reset the counter here)
    }
});

В качестве альтернативы, я сделал что-то подобное, где я сбрасывал таймер каждый раз, когда «слышал» от пользователя. Когда таймер истек, он вызвал мое действие. Таким образом, таймер постоянно пытается отключиться, но если пользователь что-то делает, таймер сбрасывается. В конце концов, пользователь перестает двигаться достаточно долго, чтобы таймер мог вызвать событие.


EDIT:
По линиям:
google.maps.event.addListener(map, 'bounds_changed', userInput);

function userInput() {
    resetTimer();
}

Где resetTimer () очищает / запускает ваш таймер. Это будет выглядеть примерно так:

var inputTimer = null;
var timeLimit = 500;

function resetTimer() {
    if(inputTimer != null) clearInterval(inputTimer);
    inputTimer = setTimeout('contactServer()', timeLimit);
}

function contactServer() {
    // here goes an ajax call
}

Я не проверял, что это компилируется, но он должен дать вам основную идею. Этот код имеет то преимущество, что он достаточно модульный, чтобы работать на многих других языках с незначительными правками. Я придерживаюсь аналогичной логики в ActionScript. Кроме того, его чрезвычайно легко читать, логически следовать и поддерживать (через 6 месяцев, когда вы забудете, как работает ваш код).

Я надеюсь, что это поможет,

- gMale

...