Использование функции подчеркивания debounce внутри закрытия

Я использую замыкания, чтобы убедиться, что действует только одно из многих событий, связанных с элементом, например: $('.textInputs').on('keyup.my.evt change.my.evt', once(options));

Я использую подчеркивание _.debounce внутри закрытия функции once. Все работает, если я вношу изменения в один ввод и жду, прежде чем внести изменения в другой ввод. Если я внесу изменение в один ввод и быстро перейду к следующему и внесу изменение за секунду до истечения времени ожидания на первом вводе, то функция debounced будет выполняться только для второго ввода. Это говорит мне, что оба входа используют одну и ту же функцию, возвращенную из debounce, но я не уверен, как изменить структуру моего кода, чтобы каждый вход использовал свою собственную функцию debounce.

Функция устранения отказов:

var doStuff = function(eleObj, options){ console.log(eleObj, options); } // the function that does stuff
var debouncedDoStuff = _debounce(doStuff, 2000);  // debouncing doStuff()

Определение однократного закрытия:

var once = function (options) {
     var eventType = null;
     function doOnce(e) {
          if (!eventType) {
               eventType = e.type; // only the first eventType we get will be registered
          }
          if (e.type == eventType) {
               debouncedDoStuff($(this), options); // calling the debounced do stuff func
          }
    }
    return doOnce;
}

Документ готов:

$(document).ready(function(){
   var options = {foo:'bar', baz:'biz'};
   $('.textInputs').on('keyup.my.evt change.my.evt', once(options));
});

Я сделал скрипт, демонстрирующий проблему: https://jsfiddle.net/enzt7q90/

Пояснения Приведенный выше код был сокращен до минимума, показывающего проблему. Реальная цель состоит в том, чтобы doStuff был присоединен к процессу ajax, который сохраняет любые изменения, внесенные в табличные данные.

Я также должен отметить, что замыкание Once не похоже на .one() в jQuery. Например, .one('keyup change', function(){}) будет выполнять функцию дважды, когда нажата клавиша табуляции (один раз для нажатия клавиши и еще раз для изменения), тогда как способ, которым я использую однократное закрытие, функция только exec'd один раз для этих двух событий.


person Drew    schedule 04.11.2020    source источник
comment
это потому, что функция debounce удаляет первый зарегистрированный элемент и регистрирует новый, если вы нажали другую кнопку. пожалуйста, отредактируйте свой пост и скажите, чего вы пытаетесь достичь.   -  person Boudy hesham    schedule 04.11.2020
comment
Я хочу, чтобы doStuff выполнялся для всех входов, у которых было событие keyup или change, когда истекает 2-секундный таймер debounce.   -  person Drew    schedule 04.11.2020
comment
тогда вам нужно будет добавить эту функцию debounce к каждому элементу, я действительно ничего не знаю о jquery, но, как я предполагаю, этот код выполняется для всех элементов в целом. Обратите внимание, что функция debounce возвращает функцию, поэтому всякий раз, когда элемент нажимается, она изменяет внешнюю переменную в замыкании, поэтому вы хотите вызвать debounce для каждого элемента.   -  person Boudy hesham    schedule 04.11.2020
comment
Я нашел решение, сохранив ссылку на отмененную функцию в стеке данных элементов. Это кажется неуклюжим, поэтому я надеюсь, что кто-то может показать правильную структуру. (Отредактированная скрипта: jsfiddle.net/w85aef1o)   -  person Drew    schedule 05.11.2020


Ответы (1)


Вы создаете отклоненную версию doStuff, передавая ее через _.debounce. Ваша проблема в том, что вы делаете это только один раз, поэтому существует только одна такая отклоненная версия, которую обработчик событий должен использовать между всеми элементами. Устранение дребезга означает, что только некоторые вызовы действительно доходят до исходной функции. По сути, вы скрываете все звонки, кроме самого последнего от doStuff.

У меня складывается впечатление, что к тому времени, когда вы фактически активируете doStuff, вы хотите принять во внимание все элементы ввода, в которых произошли изменения. Вы хотите обработать каждый из этих элементов только один раз, независимо от того, сколько раз он запускал ваш обработчик событий. Это означает, что обработка (doStuff) должна быть отменена, как и вы, но обработчик непосредственного события (doOnce) должен каким-то образом различать входные элементы.

Одно возможное решение уже было прокомментировано Боуди Хешамом, т. е. зарегистрировать отдельный обработчик событий с отдельной версией doStuff для каждого входа:

var doStuff = function(eleObj, options){ console.log(eleObj, options); }  // same as before

function once(options) {
    // Note that the debounced function is now generated inside the
    // event handler factory.
    var debouncedDoStuff = _.debounce(doStuff, 2000);
    function doOnce(e) {
        // We don't need to restrict the event type anymore, because
        // this event handler is specific to a single input element.
        // The debounce already ensures that `doStuff` is called only once
        // for this element.
        debouncedDoStuff($(this), options);
    }
    return doOnce;
}

$(document).ready(function(){
    var options = {foo:'bar', baz:'biz'};
    // Using jQuery's .each to generate an event handler for each input
    // element individually.
    $('.textInputs').each(function() {
        $(this).on('keyup.my.evt change.my.evt', once(options));
    });
});

Другой вариант — сохранить список всех элементов ввода, вызвавших событие, и дедуплицировать их, когда вы, наконец, запустите doStuff после тайм-аута:

var doStuff = function(elementList, options){
    var uniqueElements = _.uniq(elementList);
    console.log(uniqueElements, options);
}

var debouncedDoStuff = _debounce(doStuff, 2000); // as original.

function once(options) {
    var targetList = [];
    function doOnce() {
        targetList.push($(this));
        debouncedDoStuff(targetList, options);
        // Note that again, I don't restrict the callback to a single
        // event type, because the debouncing and the deduplication
        // already ensure that each input is processed only once.
        // This also prevents a problem that might ignore a 'keyup' event
        // in one element if 'change' was already triggered in another.
    }
    return doOnce;
}

// The next part is the same as your original code.
$(document).ready(function(){
   var options = {foo:'bar', baz:'biz'};
   $('.textInputs').on('keyup.my.evt change.my.evt', once(options));
});

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

Третьим вариантом может быть использование фреймворка для структурирования обработки событий обычным способом. Backbone — это пример фреймворка, который хорошо сочетается с jQuery и Underscore и хорошо справляется с такими ситуациями. Однако знакомство с фреймворком выходит за рамки этого ответа.

person Julian    schedule 04.11.2020
comment
Спасибо за варианты и пояснения. Все имеет смысл и кажется таким очевидным задним числом. - person Drew; 05.11.2020