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:
- De functie
double
wordt gegeven aan de functiemap
door de beller. - De functie
map
nodig heeft om de functie aan te roependouble
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 setsthis
naar wat werd doorgegeven aanbind
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 implementerenfunction 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)