Передача функций и колбэки

В прошлом уроке мы увидели, что функция в JavaScript – обычное значение. А значит её можно не только хранить в переменной, но и передать другой функции.

Зачем? Чтобы передать ей не готовое значение, а действие, которое она сможет выполнить сама.

Функцию можно передать другой функции

До сих пор аргументами были числа и строки: double(5), greet('Ира'). Но иногда функции нужно передать не готовые данные, а действие, которое она должна выполнить. Поскольку функция в JavaScript – тоже значение, её можно передать аргументом:

run-twice.jsjs
function runTwice(action) {  action();  action();}function sayHi() {  console.log('Привет!');}runTwice(sayHi);
Консоль
Привет!
Привет!

В runTwice(sayHi) мы не вызываем sayHi. Мы передаём саму функцию. Внутри runTwice она оказывается в параметре action, и уже runTwice решает, когда её вызвать – в нашем случае два раза.

Если хотим передать саму функцию, пишем её имя без скобок: sayHi – функция, sayHi() – результат её вызова.

Запись runTwice(sayHi()) делает другое: сначала вызовется sayHi(), а в runTwice уйдёт результат этого вызова.

Передаём функцию
runTwice(sayHi);
Передаём результат вызова
runTwice(sayHi());

Пока мы создали sayHi отдельно, дали ей имя, а затем передали в runTwice. Это удобно, если функция понадобится ещё где-нибудь.

Но что, если это действие нужно только здесь?

Функцию можно создать прямо в аргументе

Объявлять её отдельно необязательно: Function Expression можно написать прямо там, где требуется значение:

inline.jsjs
function runTwice(action) {  action();  action();}runTwice(function () {  console.log('Привет!');});
Консоль
Привет!
Привет!

Здесь мы создали функцию и сразу передали её в runTwice – отдельного имени ей не понадобилось.

Колбэк

У такого приёма есть своё название. Функцию, которую передают другой функции, чтобы та могла её вызвать, называют callback, или колбэком:

callback.jsjs
function runTwice(action) {  action();  action();}const sayHi = function () {  console.log('Привет!');};runTwice(sayHi);  // колбэк – sayHi// колбэк – функция, созданная на местеrunTwice(function () {  console.log('Пока!');});
Консоль
Привет!
Привет!
Пока!
Пока!

В обоих вызовах переданная функция – колбэк, а runTwice – функция, которая его получает и решает, когда вызвать. Здесь оба колбэка – Function Expression: sayHi сохранена в переменной, вторая создана прямо в аргументе.

Точно так же колбэком может быть и Function Declaration:

named-callback.jsjs
function sayBye() {  console.log('До встречи!');}runTwice(sayBye);
Консоль
До встречи!
До встречи!

Важно: колбэк не обязательно запускается «когда-нибудь позже». Он может быть вызван сразу, как здесь, или позже – например, после клика пользователя или завершения таймера.

Функция управляет «когда», колбэк – «что делать»

repeat.jsjs
function repeat(n, action) {  for (let i = 1; i <= n; i++) {    action(i);  }}const showStep = function (i) {  console.log(`Шаг ${i}`);};repeat(3, showStep);
Консоль
Шаг 1
Шаг 2
Шаг 3

Обратите внимание: repeat не просто вызывает action, а передаёт ему текущее значение i. Поэтому колбэк получает данные от функции, которая им управляет.

repeat знает, когда и сколько раз выполнить действие: for (let i = 1; i <= n; i++). Но она не знает, что именно делать на каждом повторении – это действие ей передают отдельно. Поэтому одну и ту же repeat можно использовать с разными функциями:

show-square.jsjs
const showSquare = function (i) {  console.log(i * i);};repeat(3, showSquare);
Консоль
1
4
9

Один код управляет процессом, другой определяет действие. Как вызов уходит внутрь repeat и возвращается – по шагам:

Вызов функции через параметр
function repeat(n, action) {
for (let i = 1; i <= n; i++) {
action(i);
}
}
const showStep = function (i) {
Создали функцию и сохранили её в переменной showStep.
console.log(`Шаг ${i}`);
};
repeat(3, showStep);

// теперь вы знаете

  1. Если нужно передать саму функцию, пишем её без (); со скобками функция сначала вызывается
  2. Функцию можно создать прямо в аргументе, если отдельное имя ей не понадобится
  3. Колбэк – функция, переданная другой функции, чтобы та могла её вызвать
  4. Колбэком может быть и Function Declaration, и Function Expression
  5. Принимающая функция может решать, когда вызвать колбэк, сколько раз его вызвать и какие аргументы ему передать

Проверьте себя

Что в этом вызове является колбэком?

function repeat(n, action) {
for (let i = 1; i <= n; i++) {
action(i);
}
}
const showStep = function (i) {
console.log(`Шаг ${i}`);
};
repeat(3, showStep);

Сколько раз будет вызвана sayHi?

function runIf(allowed, action) {
if (allowed) {
action();
}
}
function sayHi() {
console.log('Привет!');
}
runIf(false, sayHi);
runIf(true, sayHi);

В каких вызовах в параметр action попадёт функция?

function runTwice(action) {
action();
action();
}
function sayHi() {
console.log('Привет!');
}

Выбери все верные варианты

В каком порядке появятся строки в консоли?

function run(action) {
console.log('до');
action();
console.log('после');
}
run(function () {
console.log('внутри');
});

Что напечатает программа?

function withName(action) {
action('Ира');
}
withName(function (name) {
console.log('Привет, ' + name);
});
переключают уроки · не работают при фокусе в полях ввода