Szukaj…
Proste przykłady użycia wywołania zwrotnego
Oddzwanianie oferuje sposób na rozszerzenie funkcjonalności funkcji (lub metody) bez zmiany jej kodu. Takie podejście jest często stosowane w modułach (bibliotekach / wtyczkach), których kodu nie należy zmieniać.
Załóżmy, że napisaliśmy następującą funkcję, obliczającą sumę podanej tablicy wartości:
function foo(array) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
Załóżmy teraz, że chcemy coś zrobić z każdą wartością tablicy, np. Wyświetlić ją za pomocą alert()
. Możemy wprowadzić odpowiednie zmiany w kodzie foo
, takie jak to:
function foo(array) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
alert(array[i]);
sum += array[i];
}
return sum;
}
Ale co, jeśli zdecydujemy się użyć console.log
zamiast alert()
? Oczywiście zmiana kodu foo
, ilekroć zdecydujemy się zrobić coś z każdą wartością, nie jest dobrym pomysłem. O wiele lepiej jest mieć możliwość zmiany zdania bez zmiany kodu foo
. To jest dokładnie przypadek użycia dla połączeń zwrotnych. Musimy jedynie nieznacznie zmienić podpis i foo
:
function foo(array, callback) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
callback(array[i]);
sum += array[i];
}
return sum;
}
A teraz jesteśmy w stanie zmienić zachowanie foo
po prostu poprzez zmianę jego parametrów:
var array = [];
foo(array, alert);
foo(array, function (x) {
console.log(x);
});
Przykłady z funkcjami asynchronicznymi
W jQuery metoda $.getJSON()
do pobierania danych JSON jest asynchroniczna. Dlatego przekazanie kodu w wywołaniu zwrotnym zapewnia, że kod zostanie wywołany po pobraniu JSON.
Składnia $.getJSON()
:
$.getJSON( url, dataObject, successCallback );
Przykład kodu $.getJSON()
:
$.getJSON("foo.json", {}, function(data) {
// data handling code
});
Poniższe nie działałoby, ponieważ kod obsługi danych prawdopodobnie zostałby wywołany przed faktycznym otrzymaniem danych, ponieważ funkcja $.getJSON
zajmuje nieokreślony czas i nie wstrzymuje stosu wywołań w oczekiwaniu na JSON.
$.getJSON("foo.json", {});
// data handling code
Innym przykładem funkcji asynchronicznej jest funkcja animate()
jQuery. Ponieważ uruchomienie animacji zajmuje określony czas, czasem pożądane jest uruchomienie kodu bezpośrednio po animacji.
.animate()
:
jQueryElement.animate( properties, duration, callback );
Na przykład, aby utworzyć animację wygaszania, po której element całkowicie zniknie, można uruchomić następujący kod. Zwróć uwagę na użycie wywołania zwrotnego.
elem.animate( { opacity: 0 }, 5000, function() {
elem.hide();
} );
Pozwala to ukryć element zaraz po zakończeniu wykonywania funkcji. Różni się to od:
elem.animate( { opacity: 0 }, 5000 );
elem.hide();
ponieważ ten ostatni nie czeka na zakończenie animate()
(funkcja asynchroniczna), a zatem element jest od razu ukryty, powodując niepożądany efekt.
Co to jest oddzwanianie?
Jest to normalne wywołanie funkcji:
console.log("Hello World!");
Kiedy wywołujesz normalną funkcję, wykonuje swoją pracę, a następnie zwraca kontrolę z powrotem do osoby dzwoniącej.
Czasami jednak funkcja musi zwrócić kontrolę nad dzwoniącym, aby wykonać swoje zadanie:
[1,2,3].map(function double(x) {
return 2 * x;
});
W powyższym przykładzie funkcja double
jest wywołaniem zwrotnym dla map
funkcji, ponieważ:
- Funkcja
double
jest przekazywana domap
funkcji przez osobę dzwoniącą. -
map
funkcji musi wywoływać funkcjędouble
zero lub więcej razy, aby wykonać swoje zadanie.
Tak więc map
funkcji w zasadzie zwraca kontrolę do dzwoniącego za każdym razem, gdy wywołuje funkcję double
. Stąd nazwa „callback”.
Funkcje mogą akceptować więcej niż jedno połączenie zwrotne:
promise.then(function onFulfilled(value) {
console.log("Fulfilled with value " + value);
}, function onRejected(reason) {
console.log("Rejected with reason " + reason);
});
Tutaj funkcja przyjmuje then
dwie funkcje zwrotne: onFulfilled
i onRejected
. Ponadto faktycznie wywoływana jest tylko jedna z tych dwóch funkcji zwrotnych.
Co ciekawsze, funkcja wraca then
przed wywołaniem któregoś z wywołań zwrotnych. Dlatego funkcja zwrotna może zostać wywołana nawet po zwróceniu pierwotnej funkcji.
Kontynuacja (synchroniczna i asynchroniczna)
Oddzwaniania można użyć do zapewnienia kodu do wykonania po zakończeniu metody:
/** * @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"
Powyższa metoda doSomething()
wykonywana synchronicznie z blokami wywołania zwrotnego do momentu powrotu doSomething()
, zapewniając, że wywołanie zwrotne zostanie wykonane przed przejściem interpretera.
Oddzwaniania można również używać do asynchronicznego wykonywania kodu:
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"
Te then
wywołania zwrotne są uważane kontynuacją w doSomething()
metod. Zapewnienie wywołania zwrotnego jako ostatniej instrukcji w funkcji nazywa się wywołaniem ogonowym , zoptymalizowanym przez tłumaczy ES2015 .
Obsługa błędów i odgałęzienie sterowania przepływem
Połączenia zwrotne są często używane do obsługi błędów. Jest to forma rozgałęzienia przepływu sterowania, w której niektóre instrukcje są wykonywane tylko w przypadku wystąpienia błędu:
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"
Wykonanie kodu w powyższej funkcji compare()
ma dwie możliwe gałęzie: success
gdy wartości oczekiwane i rzeczywiste są takie same, i error
gdy są różne. Jest to szczególnie przydatne, gdy przepływ sterowania powinien rozgałęzić się po instrukcji asynchronicznej:
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"
Należy zauważyć, że wiele wywołań zwrotnych nie musi się wzajemnie wykluczać - można wywoływać obie metody. Podobnie, funkcja Compare compare()
może być napisana z opcjonalnymi wywołaniami zwrotnymi (używając domyślnej wartości noop - patrz Wzorzec obiektu zerowego ).
Callbacki i „to”
Często podczas korzystania z oddzwaniania chcesz uzyskać dostęp do określonego kontekstu.
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);
Rozwiązania
Użyj
bind
bind
skutecznie generuje nową funkcję, która ustawiathis
na cokolwiek, co zostało przekazane dobind
a następnie wywołuje funkcję oryginalną.function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', function() { console.log(this.msg); }.bind(this)); // <=- bind the function to `this` }
Użyj funkcji strzałek
Strzałka funkcje automatycznie wiążą obecny
this
kontekst.function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click',() => { // <=- arrow function binds `this` console.log(this.msg); }); }
Często chcesz wywołać funkcję członka, najlepiej przekazując argumenty przekazane do zdarzenia do funkcji.
Rozwiązania:
Użyj powiązania
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); };
Użyj funkcji strzałek i operatora reszty
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); };
W szczególności dla detektorów zdarzeń DOM można zaimplementować interfejs
EventListener
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); };
Oddzwonienie za pomocą funkcji strzałki
Użycie funkcji strzałki jako funkcji oddzwaniania może zmniejszyć liczbę wierszy kodu.
Domyślna składnia funkcji strzałki to
() => {}
Może to służyć jako oddzwanianie
Na przykład, jeśli chcemy wydrukować wszystkie elementy w tablicy [1,2,3,4,5]
bez funkcji strzałki kod będzie wyglądał następująco
[1,2,3,4,5].forEach(function(x){
console.log(x);
}
Dzięki funkcji strzałki można ją zmniejszyć do
[1,2,3,4,5].forEach(x => console.log(x));
Tutaj funkcja function(x){console.log(x)}
zwrotnej function(x){console.log(x)}
jest zredukowana do x=>console.log(x)