TOC

This article has been localized into Czech by the community.

Funkce:

Zpětné volací funkce (callback function)

Funkce v JavaScriptu jsou takzvaní občané první třídy, což v podstatě znamená, že funkce může být uložena v proměnné, vrácena jako výstup z jiné funkce nebo předána do funkce jako argument. Toto je velmi silná vlastnost, kterou nenajdete ve všech programovacích jazycích.

V tomto článku se budeme zabývat částí o předávání funkce jako argumentu jiné funkci. Při tomto postupu obvykle odkazujeme na předanou funkci jako na zpětnou volací funkci (callback function). Toto může být velmi užitečná technologie a ve skutečnosti ji JavaScript také hodně používá interně, přičemž několik vestavěných objektů má funkce, které mohou přijmout zpětnou volací funkci.

Již jsme měli náhled na zpětné volací funkce v předchozím článku, kde jsme mluvili o anonymních funkcích, protože zpětná volací funkce je někdy vytvořena jako anonymní funkce. Nemusí to tak být, ale často je to praktické, protože funkce použitá jako zpětná volací funkce nemusí být jinde užitečná.

Takže, nyní, když víme, co je zpětná volací funkce, pojďme s nimi pracovat!

Předávání zpětné volací funkce

Jak bylo zmíněno, JavaScript přijme zpětnou volací funkci pro mnoho metod nalezených na vestavěných objektech. Skvělým příkladem toho je objekt Array (pole), který používá zpětné volání poměrně často, například pro metodu Array.filter(). Metoda filter() nám umožní vytvořit filtrovanou verzi existujícího pole, kde je položka zahrnuta pouze v případě, že předaná zpětná volací funkce ji schválí.

Řekněme tedy, že máte pole s ovocem, ale chcete pouze ovoce začínající na písmeno A. Můžeme poté vytvořit funkci, která rozhodne, zda ovoce začíná na A, a vrátí true (pravda) nebo false (nepravda) v závislosti na výsledku, takto:

function StartsWithA(fruit)
{
	return fruit[0] == "A";
}

Tato jednoduchá funkce pouze kontroluje první písmeno předaného ovoce a vrátí true (pravda), pokud je to písmeno "A" - jinak vrátí false (nepravda). Tato funkce může být nyní předána metodě filter(), která ji zavolá pro každou položku v poli a vrátí nové pole na základě toho, které ovoce test prošlo. Zde je kompletní příklad:

function StartsWithA(fruit)
{
	return fruit[0] == "A";
}

let fruits = ["Apple", "Orange", "Pineapple", "Avocado"];

let fruitsStartingWithA = fruits.filter(StartsWithA);

// Result: Apple,Avocado
alert(fruitsStartingWithA);

Všimněte si, jak mohu jednoduše předat funkci StartsWithA jako parametr metodě filter() pouze odkazem na její název - JavaScript opravdu usnadňuje použití zpětných volacích funkcí!

Jak bylo zmíněno, pokud si myslíte, že funkci StartsWithA() nebudete potřebovat pro jiné účely, můžete ji předat jako anonymní funkci, čímž uvolníte globální prostor o jednu funkci méně, která by mohla zbytečně komplikovat věci, a možná udělat váš kód čitelnějším:

let fruits = ["Apple", "Orange", "Pineapple", "Avocado"];

let fruitsStartingWithA = fruits.filter(function(fruit)
{
	return fruit[0] == "A";
});

// Result: Apple,Avocado
alert(fruitsStartingWithA);

Jak vidíte, při volání metody filter() předávám funkci vytvořenou na místě, bez názvu, protože bude použita pouze pro tento konkrétní účel. To je kratší, ale může to být ještě kratší, díky šipkovým funkčním výrazům, což by pravděpodobně byl vhodnější přístup v tomto případě, kde je naše funkce stejně jen jednořádková:

let fruits = ["Apple", "Orange", "Pineapple", "Avocado"];

let fruitsStartingWithA = fruits.filter(fruit => fruit[0] == "A");

// Result: Apple,Avocado
alert(fruitsStartingWithA);

Krátké, sladké a jednoduché, a i když to nemusí vypadat moc jako náš počáteční příklad, stále de facto používáme zpětnou volací funkci - jen se změnila syntaxe.

Vytváření a používání zpětných volacích funkcí

Z výše uvedených příkladů můžete vidět, že předání zpětné volací funkce existující funkci je velmi jednoduché, ale je stejně jednoduché vytvořit funkci, která přijímá a používá zpětnou volací funkci? Ve skutečnosti ano - je to opravdu snadné! Nejprve se podívejme, jak můžeme definovat funkci s parametrem zpětné volací funkce:

function FunctionA(callback)
{
	callback();
}

function FunctionB()
{
	alert("Hello from FunctionB!");
}

FunctionA(FunctionB);

V tomto příkladě je FunctionA hlavní funkcí, která přijímá zpětnou volací funkci jako parametr. FunctionB bude zpětnou volací funkcí předanou do FunctionA. Protože funkce jsou v JavaScriptu občany první třídy, parametr, který přijímá zpětnou volací funkci, vypadá jako jakýkoli jiný parametr a může být z volající funkce volán jako jakákoli jiná funkce.

S tím na místě, pojďme vytvořit více ilustrativní příklad. Vytvoříme si vlastní funkci pro filtrování pole, která přijme zpětnou volací funkci, a poté necháme zpětnou volací funkci rozhodnout, zda může být položka zahrnuta do konečného výsledku, nebo ne. Takto by to mohlo vypadat:

function FilterArray(array, callback)
{
	let result = [];
	for(let item of array)
	{
		if(callback(item))
			result.push(item);
	}
	return result;
}

Naše metoda filtru přijme pole jako první parametr a zpětnou volací funkci jako svůj druhý parametr. Poté projde polem a pro každou položku v něm zavolá dodanou zpětnou volací funkci a předá jí položku. Zpětná volací funkce by pak měla vrátit true (pravda) nebo false (nepravda) v závislosti na tom, zda považuje položku za hodnou zahrnutí.

Je skvělé, že můžete z funkce filtru získat úplně odlišné chování jednoduše dodáním vlastní, uživatelsky definované zpětné volací funkce. Pojďme to zkusit:

let fruits = ["Apple", "Orange", "Pineapple", "Avocado"];

function FilterArray(array, callback)
{
	let result = [];
	for(let item of array)
	{
		if(callback(item))
			result.push(item);
	}
	return result;
}


function StartsWithA(fruit)
{
	return fruit[0] == "A";
}

function HasLongName(fruit)
{
	return fruit.length > 5;
}

// Apple,Avocado
alert(FilterArray(fruits, StartsWithA));
// Orange,Pineapple,Avocado
alert(FilterArray(fruits, HasLongName));

Jak vidíte, použil jsem funkci StartsWithA() z předchozího příkladu a pak přidal funkci HasLongName(). Používají různou logiku pro rozhodnutí, zda je položka relevantní. V posledních řádcích příkladu můžete vidět, jak volám stejnou funkci FilterArray(), ale protože používám různé zpětné volací funkce, dostávám z ní také různé výsledky. Místo toho, abych měl logiku ve funkci filtru, udělal jsem funkci více obecnou tím, že jsem jí umožnil přijímat logiku z externího zdroje prostřednictvím zpětné volací funkce.

Asynchronní operace a zpětné volání

Další situací, kde jsou zpětná volání opravdu užitečná, je, když se zabýváte asynchronními operacemi. Velmi běžným příkladem je, když chcete, aby váš (klientský) JavaScriptový kód interagoval s prostředky na vašem serveru, např. načítal nějaká data generovaná na počkání, možná na základě vstupu uživatele.

Při tomto postupu obvykle provedete asynchronní volání na server – to umožní prohlížeči pokračovat v provádění vašeho kódu namísto zastavení a čekání na odpověď serveru. Ale někdy máte kód, který závisí na dokončení volání na server, než může pokračovat, a pro situace jako tyto jsou zpětná volání opravdu užitečná.

Podívejme se na příklad. Kdybychom neměli zpětná volání, mohlo by to vypadat nějak takto:

function DownloadFile(url)
{
	console.log("Downloading file...");
	setTimeout(function()
	{
		console.log("File downloaded - ready for processing!");
		return "/local-file.png";
	}, 2000);
}

function ProcessFile(path)
{
	console.log("Processing file: " + path);
}

let path = DownloadFile("https://www.google.com/logo.png");
ProcessFile(path);

Máme funkci pro stahování souboru. Používám funkci setTimeout() k fiktivnímu volání asynchronní funkce stahování, abych zjednodušil příklad. Jakmile je soubor "stažen", vrátíme lokální cestu volajícímu. Po volání funkce DownloadFile() zavoláme funkci ProcessFile(), včetně lokální cesty k souboru.

Pokud spustíte tento příklad, budou velmi zřejmé dva hlavní problémy: Nemůžeme skutečně vrátit lokální cestu z funkce DownloadFile(), protože funkce je ukončena hned poté, co bylo provedeno asynchronní volání, a proto nevrací nic. A protože nečekáme, až se funkce DownloadFile() dokončí, než zavoláme ProcessFile(), protože ve skutečnosti se pokoušíme soubor zpracovat předtím, než je úplně stažen, jak je ilustrováno výstupem:

"Downloading file..."
"Processing file: undefined"
"File downloaded - ready for processing!"

Opravme to tím, že místo toho použijeme zpětné volání - zde je upravená verze příkladu:

function DownloadFile(url, callback)
{
	console.log("Downloading file...");
	setTimeout(function()
	{
		console.log("File downloaded - ready for processing!");
		callback("/local-file.png");
	}, 2000);
}

function ProcessFile(path)
{
	console.log("Processing file: " + path);
}

DownloadFile("https://www.google.com/logo.png", ProcessFile);

Toto opravuje oba naše problémy - jednoduše předáme funkci ProcessFile() jako zpětné volání do funkce DownloadFile(), která ji zavolá, když bude soubor stažen, a dokonce předá lokální cestu. Zkuste spustit příklad a uvidíte z výstupu, že všechno nyní funguje podle očekávání:

"Downloading file..."
"File downloaded - ready for processing!"
"Processing file: /local-file.png"

Nyní jsme vyřešili náš problém pomocí zpětného volání, a pokud se mě zeptáte, kód je ještě elegantnější.

Shrnutí

Protože funkce v JavaScriptu jsou nazývány „občané první třídy“, můžeme předávat funkci jako parametr do jiné funkce. Když tak učiníme, označujeme první funkci jako funkci zpětného volání, a jak je ilustrováno v tomto článku, zpětná volání jsou velmi užitečná pro mnohé situace.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!