Funkcja zwrotna to po prostu funkcja, którą przekazujesz do innej funkcji, aby funkcja mogła ją wywołać w późniejszym czasie. Jest to często widoczne w asynchronicznych interfejsach API ; wywołanie API wraca natychmiast, ponieważ jest asynchroniczne, więc przekazujesz do niego funkcję, którą interfejs API może wywołać po zakończeniu wykonywania zadania asynchronicznego.
Najprostszym przykładem, jaki przychodzi mi do głowy w JavaScript, jest setTimeout()
funkcja. Jest to funkcja globalna, która przyjmuje dwa argumenty. Pierwszy argument to funkcja zwrotna, a drugi argument to opóźnienie w milisekundach. Funkcja została zaprojektowana tak, aby czekać przez odpowiednią ilość czasu, a następnie wywołać funkcję zwrotną.
setTimeout(function () {
console.log("10 seconds later...");
}, 10000);
Być może widziałeś już powyższy kod, ale po prostu nie zdawałeś sobie sprawy, że funkcja, którą przekazujesz, nazywa się funkcją zwrotną. Moglibyśmy przepisać powyższy kod, aby był bardziej oczywisty.
var callback = function () {
console.log("10 seconds later...");
};
setTimeout(callback, 10000);
Wywołania zwrotne są używane w Node w każdym miejscu, ponieważ Node jest zbudowany od podstaw, aby był asynchroniczny we wszystkim, co robi. Nawet podczas rozmowy z systemem plików. Dlatego mnóstwo wewnętrznych interfejsów API Node akceptuje funkcje zwrotne jako argumenty, zamiast zwracać dane, które można przypisać do zmiennej. Zamiast tego wywoła funkcję zwrotną, przekazując żądane dane jako argument. Na przykład możesz użyć fs
biblioteki Node do odczytania pliku. fs
Moduł wystawia dwie unikalne funkcje API: readFile
a readFileSync
.
readFile
Funkcja jest asynchroniczny, gdy readFileSync
nie jest oczywisty. Możesz zobaczyć, że zamierzają używać wywołań asynchronicznych, gdy tylko jest to możliwe, ponieważ je wywołali, readFile
a readFileSync
zamiast readFile
i readFileAsync
. Oto przykład użycia obu funkcji.
Synchroniczny:
var data = fs.readFileSync('test.txt');
console.log(data);
Powyższy kod blokuje wykonywanie wątku, dopóki cała zawartość nie test.txt
zostanie wczytana do pamięci i zapisana w zmiennej data
. W węźle jest to zwykle uważane za złą praktykę. Są jednak chwile, kiedy jest to przydatne, na przykład podczas pisania krótkiego, małego skryptu, aby zrobić coś prostego, ale żmudnego, i nie zależy ci zbytnio na oszczędzaniu każdej nanosekundy, jaką możesz.
Asynchroniczne (z wywołaniem zwrotnym):
var callback = function (err, data) {
if (err) return console.error(err);
console.log(data);
};
fs.readFile('test.txt', callback);
Najpierw tworzymy funkcję zwrotną, która przyjmuje dwa argumenty err
i data
. Jednym z problemów związanych z funkcjami asynchronicznymi jest to, że przechwytywanie błędów staje się trudniejsze, więc wiele funkcji API w stylu wywołań zwrotnych przekazuje błędy jako pierwszy argument funkcji wywołania zwrotnego. Najlepiej jest sprawdzić, czy err
ma wartość, zanim zrobisz cokolwiek innego. Jeśli tak, zatrzymaj wykonywanie wywołania zwrotnego i zarejestruj błąd.
Połączenia synchroniczne mają przewagę, gdy występują wyjątki, ponieważ można je po prostu złapać za pomocą try/catch
bloku.
try {
var data = fs.readFileSync('test.txt');
console.log(data);
} catch (err) {
console.error(err);
}
W funkcjach asynchronicznych nie działa to w ten sposób. Wywołanie API wraca natychmiast, więc nie ma nic do złapania z try/catch
. Właściwe asynchroniczne interfejsy API, które używają wywołań zwrotnych, zawsze wychwytują własne błędy, a następnie przekazują je do wywołania zwrotnego, gdzie można je obsłużyć według własnego uznania.
Oprócz wywołań zwrotnych istnieje jednak inny popularny styl interfejsu API, który jest powszechnie używany, zwany obietnicą. Jeśli chcesz o nich poczytać, możesz przeczytać cały wpis na blogu, który napisałem na podstawie tej odpowiedzi tutaj .