Как перезаписать/отменить предыдущее обещание, вызываемое по одному?

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

Я придумал этот код, который игнорирует предыдущее обещание:

var scroll_callback_counter = 0;
function move_cursor_visible() {
    var cursor = self.find('.cursor');
    var i = scroll_callback_counter++;
    return cursor.is_fully_in_viewport(self).then(function(visible) {
        if (i === scroll_callback_counter && !visible) {
            var offset = cursor.offset();
            var container_offset = self.offset();
            self.scrollTop(offset.top - container_offset.top - 5);
            return true;
        }
    });
}

Есть ли способ лучше? Это работает, но я не знаю, правильный ли это способ создания кода, который отменяет/игнорирует обратный вызов обещания.

РЕДАКТИРОВАТЬ:

Вот функция is_fully_in_viewport:

function jquery_resolve(value) {
    var defer = jQuery.Deferred();
    defer.resolve(value);
    return defer.promise();
}
$.fn.is_fully_in_viewport = (function() {
    function is_visible(node, container) {
        var box = node.getBoundingClientRect();
        var viewport = container[0].getBoundingClientRect();
        var top = box.top - viewport.top;
        var bottom = box.bottom - viewport.top;
        var height = container.height();
        return bottom > 0 && top <= height;
    }
    if (window.IntersectionObserver) {
        return function(container) {
            var node = this[0];
            var defer = jQuery.Deferred();
            var item_observer = new window.IntersectionObserver(function(entries) {
                defer.resolve(entries[0].isIntersecting);
                item_observer.unobserve(node);
            }, {
                root: container[0]
            });
            item_observer.observe(node);
            return defer.promise();
        };
    } else {
        return function(container) {
            return jquery_resolve(is_visible(this[0], container));
        };
    }
})();

person jcubic    schedule 06.04.2019    source источник
comment
Предотвращение запуска обратного вызова путем проверки того, является ли он текущим, кажется лучшим, что вы можете сделать. Если вы на самом деле хотите отменить работу, которую выполняет ваш плагин, это очень сильно зависит от того, что он на самом деле делает и почему он вообще использует промисы - не могли бы вы опубликовать/связать функцию is_fully_in_viewport?   -  person Bergi    schedule 07.04.2019
comment
@Bergi is_fully_in_viewport нужно вернуть обещание, потому что он использует Intersection Observer, чтобы проверить, находится ли элемент в поле зрения.   -  person jcubic    schedule 07.04.2019
comment
Думаю, в этом случае я бы полностью отказался от обещаний. Передайте обратный вызов в is_full_in_viewport и используйте только один экземпляр IntersectionObserver для каждого элемента jQuery.   -  person Bergi    schedule 07.04.2019


Ответы (1)


Создает отмененную функцию, которая откладывает вызов func до тех пор, пока не истечет миллисекунды ожидания с момента последнего вызова отмененной функции.

Вы можете использовать debounce метод loadash,

_.debounce(func, [wait=0], [options={}])

Функция debounced поставляется с методом отмены для отмены отложенных вызовов функций и методом сброса для их немедленного вызова.

person Code Maniac    schedule 06.04.2019
comment
Спасибо, проверим. Напрочь забудьте об этой функции, возможно, она подойдет для моего случая. - person jcubic; 07.04.2019
comment
Устранение дребезга работает только в том случае, если is_fully_in_viewport гарантированно выполняется в течение фиксированного периода времени. - person Bergi; 07.04.2019