JS Tower
JavaScript & TypeScriptМодуль 2: Функции

Callbacks

Функции как аргументы других функций

Цель урока

В этом уроке ты научишься:

  • Передавать функции как аргументы
  • Понимать callback-функции
  • Видеть проблему callback hell

Что такое callback

Callback — это функция, которая передаётся в другую функцию как аргумент и вызывается позже.

function greet(name, callback) {
  console.log("Привет, " + name + "!");
  callback();
}

function sayBye() {
  console.log("Пока!");
}

greet("Иван", sayBye);
// "Привет, Иван!"
// "Пока!"

Зачем нужны callbacks

1. Обработка событий

document.addEventListener("click", function() {
  console.log("Клик!");
});

2. Асинхронные операции

setTimeout(function() {
  console.log("Прошла 1 секунда");
}, 1000);

3. Методы массивов

let numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(num) {
  console.log(num * 2);
});
// 2, 4, 6, 8, 10

Примеры callbacks

forEach

let fruits = ["яблоко", "банан", "апельсин"];

fruits.forEach(function(fruit, index) {
  console.log(index + ": " + fruit);
});
// 0: яблоко
// 1: банан
// 2: апельсин

map

let numbers = [1, 2, 3];

let doubled = numbers.map(function(n) {
  return n * 2;
});

console.log(doubled); // [2, 4, 6]

filter

let numbers = [1, 2, 3, 4, 5, 6];

let even = numbers.filter(function(n) {
  return n % 2 === 0;
});

console.log(even); // [2, 4, 6]

Стрелочные функции как callbacks

let numbers = [1, 2, 3, 4, 5];

// Обычная функция
let doubled = numbers.map(function(n) {
  return n * 2;
});

// Стрелочная функция
let doubled = numbers.map(n => n * 2);

console.log(doubled); // [2, 4, 6, 8, 10]

Асинхронные callbacks

console.log("Начало");

setTimeout(function() {
  console.log("Таймер сработал");
}, 1000);

console.log("Конец");

// Вывод:
// "Начало"
// "Конец"
// "Таймер сработал" (через 1 секунду)

Асинхронность

Callback вызывается позже, после завершения операции. Код продолжает выполняться.


Callback Hell

Вложенные callbacks становятся нечитаемыми:

// Плохо — "пирамида смерти"
getUser(userId, function(user) {
  getOrders(user.id, function(orders) {
    getOrderDetails(orders[0].id, function(details) {
      getProduct(details.productId, function(product) {
        console.log(product);
      });
    });
  });
});

Проблема

Такой код сложно читать, отлаживать и поддерживать.

Решение — Promises и async/await

// Хорошо — с async/await
async function getProductInfo(userId) {
  const user = await getUser(userId);
  const orders = await getOrders(user.id);
  const details = await getOrderDetails(orders[0].id);
  const product = await getProduct(details.productId);
  return product;
}

Создание функций с callbacks

function fetchData(url, onSuccess, onError) {
  // Имитация запроса
  setTimeout(function() {
    if (url.startsWith("http")) {
      onSuccess({ data: "Данные загружены" });
    } else {
      onError("Неверный URL");
    }
  }, 1000);
}

fetchData(
  "https://api.example.com",
  function(result) {
    console.log("Успех:", result);
  },
  function(error) {
    console.log("Ошибка:", error);
  }
);

Практика

Задание 1: forEach

Задача: Выведи квадраты чисел массива.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
1
4
9
16
25

Задание 2: map

Задача: Преобразуй массив имён в приветствия.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
["Привет, Иван!","Привет, Мария!","Привет, Пётр!"]

Задание 3: Свой callback

Задача: Создай функцию repeat(n, callback), которая вызывает callback n раз.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
Итерация 0
Итерация 1
Итерация 2

Проверь себя

  1. Что такое callback?
  2. Почему возникает callback hell?
  3. Чем стрелочные функции удобны для callbacks?