Hoe annuleer je een debounced-functie nadat deze is aangeroepen en voordat deze wordt uitgevoerd?

Ik maak een debounced-versie van een functie met onderstrepingsteken:

var debouncedThing = _.debounce(thing, 1000);

Eenmaal debouncedThing heet…

debouncedThing();

…is er een manier om het te annuleren, tijdens de wachttijd voordat het daadwerkelijk wordt uitgevoerd?


Antwoord 1, autoriteit 100%

Als u de laatste versie van lodash gebruikt, kunt u eenvoudig het volgende doen:

// create debounce
const debouncedThing = _.debounce(thing, 1000);
// execute debounce, it will wait one second before executing thing
debouncedThing();
// will cancel the execution of thing if executed before 1 second
debouncedThing.cancel()

Een andere oplossing is met een vlag:

// create the flag
let executeThing = true;
const thing = () => {
   // use flag to allow execution cancelling
   if (!executeThing) return false;
   ...
};
// create debounce
const debouncedThing = _.debounce(thing, 1000);
// execute debounce, it will wait one second before executing thing
debouncedThing();
// it will prevent to execute thing content
executeThing = false;

Antwoord 2, autoriteit 2%

Oud, maar met een opmerking voor iedereen die hier komt.

De documenten (ik kijk nu naar 1.9.1) zeggen dat je het volgende zou moeten kunnen doen:

var fn = () => { console.log('run'); };
var db = _.debounce(fn, 1000);
db();
db.cancel();

Dit zou doen wat de OP wil doen (en wat ik wilde doen). Het zou het consolebericht niet afdrukken.

Het is me nooit gelukt om dit werkend te krijgen. Ik heb hoog en laag gezocht naar een .cancel()zoals beloofd in het Underscore-document en ik kan het niet vinden.

Als je onderstrepingsteken gebruikt, gebruik dan de vlagoptie in het geaccepteerde antwoord van Carlos Ruana. Mijn vereisten laten jammer genoeg (naar mijn mening) een upgrade (naar mijn mening) van Underscore naar Lodash niet toe. Underscore heeft minder functionaliteit, maar is functioneler dan zonder.


Antwoord 3, autoriteit 2%

de Vanilla js-variant met opzegbare wrap

Houd er rekening mee dat u voor deze oplossing geen externe debounce-functie hoeft aan te passen of zelfs een externe te gebruiken. De logica wordt gedaan in een wrapepr-functie. Debounce-code verstrekt.


De eenvoudigste manier om een ​​reeds aangeroepen functie te annuleren binnen de debounce-periode, is door deze aan te roepen vanuit een annuleerbare wrap. Voeg gewoon 3 regels code toe en een optionele voorwaarde.

const doTheThingAfterADelayCancellable = debounce((filter, abort) => {
  if (abort) return
  // here goes your code...
  // or call the original function here
}, /*debounce delay*/500)
function onFilterChange(filter) {
  let abort = false
  if (filter.length < 3) { // your abort condition
    abort = true
  }
  // doTheThingAfterADelay(filter) // before
  doTheThingAfterADelayCancellable(filter, abort) // new wrapped debounced call
}

Je annuleert het door het opnieuw aan te roepen met abort = true.

De manier waarop het werkt, is dat het de vorige time-out fn wist en een nieuwe instelt zoals altijd, maar nu met het pad if (true) return.

Je kunt het ook handmatig doen vanuit een andere code…

doTheThingAfterADelayCancellable(null, true)

…of sluit het af en bel met cancelBounce()

function cancelBounce() {
  doTheThingAfterADelayCancellable(null, true)
}

Ter referentie, dit is je klassieke debounce-functie uit Underscore. In mijn voorbeeld blijft het intact.

// taken from Underscore.js
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
export function debounce(func, wait, immediate) {
  let timeout
  return function() {
    let context = this, args = arguments
    let later = function() {
      timeout = null
      if (!immediate) func.apply(context, args)
    }
    let callNow = immediate && !timeout
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
    if (callNow) func.apply(context, args)
  }
}

Antwoord 4

Wat ik heb gedaan, is _.mixin gebruiken om een ​​_.cancellableDebounce-methode te maken. Het is bijna identiek aan het origineel, behalve twee nieuwe regels.

_.mixin({
    cancellableDebounce: function(func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        var later = function() {
          var last = _.now() - timestamp;
          if (last < wait && last >= 0) {
            timeout = setTimeout(later, wait - last);
          } else {
            timeout = null;
            if (!immediate) {
              result = func.apply(context, args);
              if (!timeout) context = args = null;
            }
          }
        };
        return function() {
          context = this;
          args = arguments;
          timestamp = _.now();
          var callNow = immediate && !timeout;
          if (!timeout) timeout = setTimeout(later, wait);
          if (callNow) {
            result = func.apply(context, args);
            context = args = null;
          }
          // Return timeout so debounced function can be cancelled
          result = result || {};
          result.timeout = timeout;
          return result;
        };
    }
});

GEBRUIK:

var thing = function() {
    console.log("hello world");
}
var debouncedThing = _.cancellableDebounce(thing, 1000);
var timeout = debouncedThing().timeout;
clearTimeout(timeout);

Other episodes