Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!

⛔ Potrzebujesz wsparcia? Oceny CV? A może Code Review? ✅ Dołącz do naszej społeczności na Discordzie!

Callback functions w JavaScript

Wyjaśnienie i przykłady użycia

Funkcja uruchamiana przez inną funkcję – to już prawie pełna definicja callbacku. Co jeszcze się pod tym kryje i do czego funkcje wywołania zwrotnego są wykorzystywane na co dzień? W tym artykule znajdziesz zasady działania callbacków oraz przykłady ich użycia w pracy programisty i w JavaScripcie.

Chcesz lepiej zapamiętać to zagadnienie? Wykonaj warsztat z JavaScript: Podstawy, aby osiągnąć swój cel!

Spis treści

 Dla kogo jest ten artykuł

Łatwiej Ci będzie zrozumieć działanie callback functions i zauważyć ich zastosowanie w JavaScripcie, jeśli masz już opanowane podstawy tego języka, czyli np. znasz obsługę zdarzeń (events). Nie jest to jednak konieczne – wystarczy znajomość składni i działania funkcji.

 Callback function – znaczenie

Callback function (lub w skrócie: callback) tłumaczymy jako „funkcja wywołania zwrotnego”. Polska nazwa może jednak skutecznie zniechęcić, więc zanim porzucisz ten temat, po prostu przejdź do przykładów!

Na początek wiedz tylko, że callback to funkcja wywołana przez drugą funkcję. To trochę jak przekazany nam numer telefonu, pod który dzwonimy i coś się wydarza, np. przyjeżdża taksówka.

 Jak działa callback function

 Krótki przykład wykorzystania callback function

Zaraz to dokładnie omówimy, ale najpierw skoczmy na głęboką wodę. Callback to w poniższym przykładzie funkcja display:

function outputSum(a, b, displayFunc) {
  const sum = a + b;
  return displayFunc(sum)
}

function display(value) {
  console.log("This is your output: ", value)
}

//wywołanie funkcji outputSum i przekazanie callbacka display:
outputSum(5, 10, display)
// Ważne: zwróć uwagę, że display tu nie wywołujemy – nie ma nawiasów okrągłych: display()

// output w konsoli:
// This is your output: 15

Powyżej możesz zauważyć dwa wyznaczniki definiujące callback:

  1. Callback to funkcja przekazywana do drugiej funkcji przez parametr:
    function outputSum(a, b, displayFunc) {
      const sum = a + b;
      displayFunc(sum)
    }
    outputSum(5, 10, display)
  2. Callback to funkcja wywoływana (uruchamiana) przez funkcję, do której ją przekazaliśmy:
    function outputSum(a, b, displayFunc) {
      const sum = a + b;
      displayFunc(sum) // miejsce wywołania
    }
    outputSum(5, 10, display) // uruchomienie

 Do czego potrzebne są callback functions

Zaraz rozłożymy na czynniki pierwsze powyższy przykład, lecz najpierw odpowiemy na pytanie: do czego potrzebne są callback functions? Do tego, by funkcje mogły wykonywać dane „czynności” w sposób określony poza nimi.

Przykładowo przygotujVegeObiad jest funkcją, do której przez parametry przekazujemy warzywa. Produkty te są dalej obrabiane. Rozwiązanie przedstawię w postaci pseudokodu dla łatwiejszego zrozumienia tematu.

function przygotujVegeObiad(warzywa) {
  kod mycia warzyw
  kod krojenia warzyw
  kod smażenia warzyw
}

W tej chwili obróbka termiczna będzie zawsze taka sama: smażenie. Jeśli chcielibyśmy coś zmienić, musielibyśmy za każdym razem modyfikować istniejącą funkcję – a to nie jest dobra praktyka. Najlepiej mieć jedno elastyczne rozwiązanie. Decydujmy więc o obróbce warzyw w momencie wywołania funkcji przygotujVegeObiad!

przygotujVegeObiad(warzywa, smaż)
przygotujVegeObiad(warzywa, gotuj)
przygotujVegeObiad(warzywa, blanszuj)

Zwróć uwagę, że żadna z funkcji przekazywanych w argumencie nie jest wywoływana (nie ma nawiasów okrągłych). Gdybyśmy tak robili, to argumentem stałoby się to, co zwraca funkcja, a nie sama funkcja.

Żeby powyższe wywołanie mogły działać, musimy dostosować funkcję przygotujVegeObiad do przyjęcia callbacku, czyli funkcji smaż, gotuj lub blanszuj.

function przygotujVegeObiad(warzywa, obrabiaj) {
  kod mycia warzyw
  kod krojenia warzyw
  obrabiaj(warzywa)
}

Deklaracje funkcji smaż, gotuj i blanszuj mogą istnieć w jakimś innym narzędziu, które nie musi nas interesować (tak się dzieje, gdy np. korzystamy z rozwiązań wbudowanych w JavaScript). Ale mogłyby wyglądać tak:

function smaż(produkty) {
  kod smażenia produktów
}
function gotuj(produkty) {
  kod gotowania produktów
}
function blanszuj(produkty) {
  kod blanszowania produktów
}

W praktyce więc, gdybyśmy chcieli ugotować warzywa, napisalibyśmy:

przygotujVegeObiad(warzywa, gotuj)

Silnik JavaScript uruchomiłby następujący kod:

kod mycia warzyw
kod krojenia warzyw
kod gotowania produktów – czyli warzyw

 Użycie callback function w kodzie JavaScript

W projektach w pracy spotkasz się z gotowymi narzędziami, które będziesz musiał „wpleść” w pisane funkcjonalności. Załóżmy, że takie narzędzie odpowiedzialne jest za prezentowanie treści użytkownikowi (na podobieństwo wcześniej napisanej funkcji display), Ty natomiast odpowiadasz za treść – wynik obliczeń funkcji outputSum.

Nie wiesz dokładnie, jak zaprezentowana zostanie użytkownikowi suma obliczeń (bo np. odpowiada za to osobny zespół), ale wiesz, jakiego narzędzia użyć.

import display from displayerTool // importujesz potrzebną funkcję z narzędzia

function outputSum(a, b, displayFunc) {
  const sum = a + b;
  displayFunc(sum) // wiesz, że skorzystasz z jakiejś funkcji wyświetlania, ale nie Ty ją tworzysz
}

// wywołujesz swoją funkcję i przekazujesz to niej funkcję zaimportowaną z narzędzia
outputSum(5, 10, display)

W ten sposób uzyskujesz np. wydruk w konsoli lub w jakąś ostylowaną i przyjazną dla użytkownika formę tekstu:

This is your output: 15

Jeśli zespół od narzędzia displayerTool zmieni sposób wyświetlania informacji, to Twój kod nadal będzie działać – nie będziesz musiał nic zmieniać. To duża oszczędność pracy i mniejsze ryzyko generowania błędów w projekcie!

Gdyby jednak jakimś sposobem nazwa funkcji display uległa zmianie, to dzięki callbackowi wystarczy, że zmienisz ją w miejscu wywołania. Nie musisz „grzebać” w ciele funkcji outputSum – dzięki temu trzymasz się dobrych praktyk.

 Callback functions wbudowane w JavaScript – przykłady

Jeśli już trochę znasz JavaScript, to być może korzystasz z callbacków nieświadomie. Zobacz popularne przykłady ich wykorzystania – powinno Ci to pomóc zrozumieć temat funkcji wywołania zwrotnego.

 Zdarzenia – addEventListener()

// tworzę przycisk
const button = document.createElement("button");
button.innerText = "Click me";
document.body.appendChild(button)

// nasłuchuję na kliknięcie
button.addEventListener('click', changeColor)

// definiuję, co ma się stać, gdy kliknę przycisk
function changeColor(e) {
  e.target.style.backgroundColor = "red"
}

Podświetliłem linię z callbackiem. Powyższy zapis może również wyglądać np. tak:

// funkcja nazwana
button.addEventListener('click', function changeColor(e) {
  e.target.style.backgroundColor = "red"
})

lub tak:

// funkcja anonimowa
button.addEventListener('click', function(e) {
  e.target.style.backgroundColor = "red"
})

lub tak:

// funkcja strzałkowa
button.addEventListener('click', e => {
  e.target.style.backgroundColor = "red"
})

Nieważne, czy stosujemy funkcję anonimową, czy strzałkową, czy przekazujemy ją razem z ciałem w parametrze, czy deklarujemy ją powyżej/poniżej i przekazujemy tylko jej nazwę. Liczą się dwie rzeczy:

  1. Callback to funkcja przekazywana do drugiej funkcji przez parametr.
  2. Callback to funkcja wywoływana (uruchamiana) przez funkcję, do której ją przekazaliśmy.

 Metody forEach() i map()

const fruit = ["melon", "pear", "kiwi"];

fruit.forEach(f => console.log("I would like to eat a: ", f));

const questions = fruit.map( function(f) {
  return "What color is a " + f + "?"
})
console.log(questions)

Podświetlone linie to miejsce użycia callback functions. Metoda map i forEach używa go w ten sposób: „dla każdego elementu podanego w parametrze wykonaj callback tyle razy, ile elementów ma tablica”.

 Funkcje czasu i metody tablicowe

Kolejne przykłady, których już nie będę tu omawiał (lecz linkuję do MDN), to chociażby:

 

Znajomość działania callbacków jest w pracy programisty niezbędna. Jak przeczytałeś powyżej, są one zarówno tworzone w projektach, jak i używane w rozwiązaniach wbudowanych w JavaScript (ich siłę zobaczysz w kodzie asynchronicznym). Nie przejmuj się, jeśli jeszcze nie do końca zrozumiałeś ten temat – z czasem się z nim oswoisz, bo callback functions są wszędzie.

Udostępnij ten artykuł:

Mentoring to efektywna nauka pod okiem doświadczonej osoby, która:

  • przekazuje Ci swoją wiedzę i nadzoruje Twoje postępy w zdobywaniu umiejętności,
  • uczy Cię dobrych praktyk i wyłapuje złe nawyki,
  • wspiera Twój rozwój i zwiększa zaangażowanie w naukę.

Mam coś dla Ciebie!

W każdy piątek rozsyłam motywujący do nauki programowania newsletter!

Dodatkowo od razu otrzymasz ode mnie e-book o wartości 39 zł. To ponad 40 stron konkretów o nauce programowania i pracy w IT.

PS Zazwyczaj wysyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu e-mail.