Skip to content

Latest commit

 

History

History
142 lines (101 loc) · 9.89 KB

File metadata and controls

142 lines (101 loc) · 9.89 KB

Функции обратного вызова

Что такое функция обратного вызова?

Давайте начнем с самого главного. Функция обратного вызова (callback function) — это не особый новый тип функции в JavaScript. Это, в первую очередь, концепция, паттерн использования функций. Если коротко, то callback — это функция, которая передается другой функции в качестве аргумента и затем вызывается внутри этой внешней функции.

Название "обратный вызов" (callback) довольно точно описывает суть: мы не вызываем функцию прямо сейчас, мы лишь передаем ссылку на нее, чтобы ее вызвали обратно, когда придет подходящее время, в нужный момент работы внешней функции.

Простой пример: передача функции как аргумента

Давайте разберем самый базовый пример, который у вас приведен. Все начинается с идеи, что функции в JavaScript являются так называемыми объектами первого класса. Это сложное название означает простую вещь: с функциями можно работать так же, как и с любыми другими значениями (числами, строками, массивами). Их можно:

  • сохранять в переменные,
  • передавать в другие функции в качестве аргументов,
  • возвращать из других функций.

Вот как это выглядит на практике:

// 1. Создаем функцию и сохраняем ее в переменную
let functionVariable = function() {
    console.log("Not so secret though.");
};

// 2. Создаем другую функцию, которая ожидает получить другую функцию в качестве аргумента
function doFlexibleStuff(executeStuff) {
    // 3. Внутри мы вызываем ту функцию, которую получили в аргументе
    executeStuff();
    console.log("Inside doFlexibleStuffFunction.");
}

// 4. Передаем первую функцию во вторую в качестве аргумента
doFlexibleStuff(functionVariable);

Что произойдет при запуске этого кода?

  1. Создается переменная functionVariable, которая хранит в себе анонимную функцию.
  2. Вызывается функция doFlexibleStuff. Вместо обычного аргумента (числа или строки) мы передаем ей саму функцию, хранящуюся в functionVariable.
  3. Код внутри doFlexibleStuff начинает выполняться.
  4. Интерпретатор доходит до строки executeStuff();. Он понимает, что executeStuff — это не встроенный метод, а аргумент, который мы передали. Он смотрит, что это за аргумент, и видит, что это — функция.
  5. Он вызывает эту функцию, то есть выполняет код console.log("Not so secret though.");.
  6. После этого выполнение кода внутри doFlexibleStuff продолжается, и выводится сообщение "Inside doFlexibleStuffFunction.".

Итоговый вывод в консоли:

Not so secret though.
Inside doFlexibleStuffFunction.

Гибкость обратных вызовов

Мощь этого подхода в его невероятной гибкости. Функция doFlexibleStuff абсолютно не заботится о том, что именно делает переданная ей функция. Ее задача — вызвать ее в нужный момент. Это позволяет нам один раз написать логику вызова, а поведение подставлять разное.

Посмотрите на второй пример:

// Создаем совершенно другую функцию
let anotherFunctionVariable = function() {
    console.log("Another anonymous function implementation.");
}

// И передаем ее в ту же самую doFlexibleStuff
doFlexibleStuff(anotherFunctionVariable);

Функция doFlexibleStuff даже не заметила подмены! Она все так же приняла аргумент и вызвала его. Она "гибкая" (flexible), как и говорит ее имя.

Вывод на этот раз:

Another anonymous function implementation.
Inside doFlexibleStuffFunction.

Зачем это все нужно? Асинхронность и встроенные функции

Вы можете спросить: "Это выглядит сложно, зачем это нужно в реальности?" Один из главных ответов — для работы с асинхронными операциями.

Асинхронность — это когда код не выполняется строго сверху вниз, построчно. Некоторые операции занимают время: например, загрузка данных с сервера, чтение файла или, как в наших примерах, обычная задержка по таймеру.

JavaScript не останавливается и не ждет, пока такая операция завершится. Он говорит: "Эй, браузер (или Node.js), выполни вот эту задачу, а когда закончишь — вызови функцию, которую я тебе передам". Передаваемая функция и есть callback.

Классический пример: setTimeout

Функция setTimeout() — идеальная иллюстрация асинхронного обратного вызова.

// 1. Создаем callback-функцию
let youGotThis = function () {
    console.log("You're doing really well, keep coding!");
};

// 2. Передаем ее в setTimeout
setTimeout(youGotThis, 1000);

Как это работает:

  1. Мы передаем функцию youGotThis и число 1000 (миллисекунд) в setTimeout.
  2. setTimeout запускает таймер и немедленно завершает свою работу, позволяя остальному коду выполняться дальше, не блокируя его.
  3. Примерно через 1000 мс, когда таймер истекает, движок JavaScript вызывает обратно (calls back) нашу функцию youGotThis.
  4. В консоль выводится сообщение.

Важнейший момент: между вызовом setTimeout и выполнением youGotThis может быть выполнен очень много другого кода. Это и есть асинхронность.

Пример: setInterval

Функция setInterval() работает похожим образом, но вызывает callback не один раз, а постоянно с заданным интервалом, пока не будет остановлена.

setInterval(youGotThis, 1000);

Этот код будет каждую секунду подбадривать вас в консоли, создавая бесконечный цикл с паузами.

Анонимные функции как callbacks

Часто, особенно с такими функциями как setTimeout, callback-функция маленькая и используется только один раз. В таком случае ее можно не объявлять отдельно в переменной, а создать прямо на месте, "анонимно".

Сравните два подхода:

Подход 1: Функция в переменной (именованная)

let myCallback = function() {
    console.log("Hello after 2 seconds!");
};
setTimeout(myCallback, 2000);

Подход 2: Анонимная функция прямо в аргументе

setTimeout(function() {
    console.log("Hello after 2 seconds!");
}, 2000);

Оба варианта работают абсолютно идентично. Второй вариант короче и читается как одно целое действие: "сделай что-то через 2 секунды". Именно поэтому анонимные callback-функции так широко распространены.

Заключение

  • Callback — это функция, переданная в другую функцию для последующего вызова.
  • Это возможно, потому что функции в JavaScript являются объектами (first-class citizens).
  • Этот подход обеспечивает гибкость и переиспользование кода.
  • Это фундаментальный механизм для работы с асинхронными операциями (таймеры, запросы к серверу, события).