Zoeken…


Voorbeelden van eenvoudig terugbellen

Callbacks bieden een manier om de functionaliteit van een functie (of methode) uit te breiden zonder de code te wijzigen . Deze aanpak wordt vaak gebruikt in modules (bibliotheken / plug-ins), waarvan de code niet zou moeten worden gewijzigd.

Stel dat we de volgende functie hebben geschreven, die de som van een bepaalde reeks waarden berekent:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        sum += array[i];
    }
    return sum;
}

Stel nu dat we iets met elke waarde van de array willen doen, bijvoorbeeld weergeven met alert() . We kunnen de juiste wijzigingen in de code van foo aanbrengen, zoals hier:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        alert(array[i]);
        sum += array[i];
    }
    return sum;
}

Maar wat als we besluiten om console.log te gebruiken in plaats van alert() ? Het is duidelijk geen goed idee om de code van foo wanneer we besluiten om iets anders met elke waarde te doen. Het is veel beter om de optie te hebben om van gedachten te veranderen zonder de code van foo te veranderen. Dat is precies het gebruik van callbacks. We hoeven de handtekening en het lichaam van foo slechts licht te wijzigen:

function foo(array, callback) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        callback(array[i]);
        sum += array[i];
    }
    return sum;
}

En nu kunnen we het gedrag van foo veranderen door de parameters ervan te wijzigen:

var array = [];
foo(array, alert);
foo(array, function (x) {
    console.log(x);
});

Voorbeelden met asynchrone functies

In jQuery is de methode $.getJSON() om JSON-gegevens op te halen asynchroon. Daarom zorgt het doorgeven van code in een callback ervoor dat de code wordt aangeroepen nadat de JSON is opgehaald.

$.getJSON() syntaxis:

$.getJSON( url, dataObject, successCallback );

Voorbeeld van $.getJSON() code:

$.getJSON("foo.json", {}, function(data) {
    // data handling code
});

Het volgende zou niet werken, omdat de gegevensverwerkingscode waarschijnlijk wordt aangeroepen voordat de gegevens daadwerkelijk worden ontvangen, omdat de functie $.getJSON een niet-gespecificeerde tijdsduur in $.getJSON neemt en de call-stack niet $.getJSON terwijl deze op de JSON wacht.

$.getJSON("foo.json", {});
// data handling code

Een ander voorbeeld van een asynchrone functie is de animate() -functie van jQuery. Omdat het een bepaalde tijd kost om de animatie uit te voeren, is het soms wenselijk om een code direct na de animatie uit te voeren.

.animate() syntaxis:

jQueryElement.animate( properties, duration, callback );

Om bijvoorbeeld een vervagende animatie te maken waarna het element volledig verdwijnt, kan de volgende code worden uitgevoerd. Let op het gebruik van de terugbelactie.

elem.animate( { opacity: 0 }, 5000, function() {
    elem.hide();
} );

Hierdoor kan het element worden verborgen direct nadat de functie is voltooid. Dit verschilt van:

elem.animate( { opacity: 0 }, 5000 );
elem.hide();

omdat de laatste niet wacht tot animate() (een asynchrone functie) is voltooid, en daarom is het element meteen verborgen, wat een ongewenst effect produceert.

Wat is een callback?

Dit is een normale functieaanroep:

console.log("Hello World!");

Wanneer u een normale functie oproept, doet deze zijn werk en keert vervolgens de besturing terug naar de beller.

Soms moet een functie echter de besturing teruggeven aan de beller om zijn werk te doen:

[1,2,3].map(function double(x) {
    return 2 * x;
});

In het bovenstaande voorbeeld is de functie double een callback de functie map omdat:

  1. De functie double wordt gegeven aan de functie map door de beller.
  2. De functie map nodig heeft om de functie aan te roepen double nul of meer keer in om zijn werk te doen.

Zo is de functie map is in wezen terug te keren controle terug naar de beller elke keer dat de functie oproepen double . Vandaar de naam "callback".


Functies kunnen meer dan één terugbelaanvraag accepteren:

promise.then(function onFulfilled(value) {
    console.log("Fulfilled with value " + value);
}, function onRejected(reason) {
    console.log("Rejected with reason " + reason);
});

Hier accepteert de functie then twee callback-functies, onFulfilled en onRejected . Bovendien wordt slechts één van deze twee callback-functies daadwerkelijk aangeroepen.

Wat interessanter is, is dat de functie then terugkeert voordat een van de callbacks wordt aangeroepen. Daarom kan een callback-functie worden aangeroepen, zelfs nadat de oorspronkelijke functie is teruggekeerd.

Vervolg (synchroon en asynchroon)

Callbacks kunnen worden gebruikt om te voorzien in code die moet worden uitgevoerd nadat een methode is voltooid:

/**
 * @arg {Function} then continuation callback
 */
function doSomething(then) {
  console.log('Doing something');
  then();
}

// Do something, then execute callback to log 'done'
doSomething(function () {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something"
//   "Done"
//   "Doing something else"

De methode doSomething() hierboven wordt synchroon uitgevoerd met de callback - uitvoeringsblokken totdat doSomething() terugkeert, zodat de callback wordt uitgevoerd voordat de interpreter verder gaat.

Callbacks kunnen ook worden gebruikt om code asynchroon uit te voeren:

doSomethingAsync(then) {
  setTimeout(then, 1000);
  console.log('Doing something asynchronously');
}

doSomethingAsync(function() {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something asynchronously"
//   "Doing something else"
//   "Done"

De then callbacks worden beschouwd als voortzettingen van de doSomething() -methoden. Terugbellen als de laatste instructie in een functie wordt een staartoproep genoemd , die wordt geoptimaliseerd door ES2015-tolken .

Foutafhandeling en control-flow branch

Callbacks worden vaak gebruikt om fouten af te handelen. Dit is een vorm van control flow branch, waarbij sommige instructies alleen worden uitgevoerd als er een fout optreedt:

const expected = true;

function compare(actual, success, failure) {
  if (actual === expected) {
    success();
  } else {
    failure();
  }
}

function onSuccess() {
  console.log('Value was expected');
}

function onFailure() {
  console.log('Value was unexpected/exceptional');
}

compare(true, onSuccess, onFailure);
compare(false, onSuccess, onFailure);

// Outputs:
//   "Value was expected"
//   "Value was unexpected/exceptional"

Code-uitvoering in compare() hierboven heeft twee mogelijke vertakkingen: success wanneer de verwachte en werkelijke waarden hetzelfde zijn, en error wanneer ze verschillend zijn. Dit is vooral handig wanneer de besturingsstroom na enige asynchrone instructie moet vertakken:

function compareAsync(actual, success, failure) {
  setTimeout(function () {
    compare(actual, success, failure)
  }, 1000);
}

compareAsync(true, onSuccess, onFailure);
compareAsync(false, onSuccess, onFailure);
console.log('Doing something else');

// Outputs:
//   "Doing something else"
//   "Value was expected"
//   "Value was unexpected/exceptional"

Opgemerkt moet worden dat meerdere callbacks elkaar niet exclusief hoeven te zijn - beide methoden kunnen worden aangeroepen. Evenzo kan de compare() worden geschreven met callbacks die optioneel zijn (door een noop als standaardwaarde te gebruiken - zie Null Object-patroon ).

Callbacks en `dit`

Vaak wilt u bij het terugbellen toegang krijgen tot een specifieke context.

function SomeClass(msg, elem) {
  this.msg = msg;
  elem.addEventListener('click', function() {
    console.log(this.msg);  // <= will fail because "this" is undefined
  });
}

var s = new SomeClass("hello", someElement);

Oplossingen

  • Gebruik bind

    bind effectief genereert een nieuwe functie die sets this naar wat werd doorgegeven aan bind roept vervolgens de oorspronkelijke functie.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', function() {
          console.log(this.msg);  
        }.bind(this));  // <=-  bind the function to `this`
      }
    
  • Gebruik pijlfuncties

    Pijlfuncties binden de huidige automatisch aan this context.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click',() => {   // <=-  arrow function binds `this`
          console.log(this.msg);  
        });
      }
    

Vaak wilt u een lidfunctie aanroepen en idealiter alle argumenten die aan de gebeurtenis zijn doorgegeven, doorgeven aan de functie.

Oplossingen:

  • Gebruik binden

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this.handleClick.bind(this));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • Gebruik pijlfuncties en de rustoperator

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', (...a) => this.handleClick(...a));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • Vooral voor DOM-event-luisteraars kunt u de EventListener interface implementeren

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this);
      }
    
      SomeClass.prototype.handleEvent = function(event) {
        var fn = this[event.type];
        if (fn) {
           fn.apply(this, arguments);
        }
      };
      
      SomeClass.prototype.click = function(event) {
        console.log(this.msg);
      };
    

Terugbellen met behulp van de pijlfunctie

Het gebruik van de pijlfunctie als terugbelfunctie kan coderegels verminderen.

De standaardsyntaxis voor de pijlfunctie is

() => {}

Dit kan worden gebruikt als callbacks

Als we bijvoorbeeld alle elementen in een array willen afdrukken [1,2,3,4,5]

zonder pijlfunctie ziet de code er zo uit

[1,2,3,4,5].forEach(function(x){
                 console.log(x);
            }

Met de pijlfunctie kan dit worden teruggebracht tot

[1,2,3,4,5].forEach(x => console.log(x));

Hier wordt de functie callback-functie function(x){console.log(x)} teruggebracht tot x=>console.log(x)



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow