Node.js - Понятие Callback

Здравствуйте,future programmers! Сегодня мы отправляемся в увлекательное путешествие в мир callbacks в Node.js. Как ваш доброжелательный сосед-преподаватель информатики, я здесь, чтобы passo passo помочь вам понять это понятие. Не волнуйтесь, если вы новички в программировании - мы начнем с азов и постепенно перейдем к более сложным темам. Так что возьмите杯咖啡(или чай, если это ваше дело), и давайте окунемся в это!

Node.js - Callbacks Concept

Что такое Callback?

Представьте, что вы находитесь в忙餐厅. Вы оставляете заказ официанту, но вместо того чтобы стоять и ждать свою еду, вы садитесь и общаетесь с друзьями. Официант "позвонит вам обратно", когда ваша еда будет готова. Это в принципе и есть callback в программировании!

В Node.js, callback - это функция, которая передается в качестве аргумента другой функции и выполняется после того, как та функция завершит свою работу. Это способ обеспечить то, что определенный код не будет выполняться, пока не будет завершена предыдущая операция.

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

function greet(name, callback) {
console.log('Hello, ' + name + '!');
callback();
}

function sayGoodbye() {
console.log('Goodbye!');
}

greet('Alice', sayGoodbye);

В этом примере, sayGoodbye - это наша callback-функция. Мы передаем ее функции greet, которая вызывает ее после вывода приветствия. Когда вы запустите этот код, вы увидите:

Hello, Alice!
Goodbye!

Callback позволяет нам контролироватьsequence of operations, обеспечивая то, что "Goodbye!" будет выведен после приветствия.

Пример блокирующего кода

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

Вот пример блокирующего кода:

const fs = require('fs');

// Blocking code
const data = fs.readFileSync('example.txt', 'utf8');
console.log(data);
console.log('File reading finished');
console.log('Program ended');

В этом примере, readFileSync - это синхронная функция, которая читает файл. Программа будет ждать, пока файл полностью не будет прочитан, прежде чем перейти к следующей строке. Если файл большой, это может вызвать заметную задержку в вашей программе.

Пример неблокирующего кода

Теперь давайте посмотрим, как мы можем использовать callbacks для того, чтобы сделать наш код неблокирующим:

const fs = require('fs');

// Non-blocking code
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log(data);
});

console.log('File reading started');
console.log('Program ended');

В этой неблокирующей версии, readFile принимает callback-функцию в качестве последнего аргумента. Эта функция вызывается, когда чтение файла завершено (или если occurs error). Программа не ждет, пока файл будет прочитан; она сразу продолжает выполнять следующие строки.

Вывод может выглядеть так:

File reading started
Program ended
[Contents of example.txt]

Обратите внимание, как "File reading started" и "Program ended" выводятся перед содержимым файла. Это потому, что чтение файла происходит асинхронно, позволяя остальной части программы продолжать выполнение.

Callback в виде箭头 функции

В的现代 JavaScript, мы часто используем arrow функции для callbacks. Они предоставляют более краткуюсинтаксис. Давайте перепишем наш пример приветствия, используя arrow функцию:

function greet(name, callback) {
console.log('Hello, ' + name + '!');
callback();
}

greet('Bob', () => {
console.log('Goodbye!');
});

Здесь, вместо того чтобы определять отдельную функцию sayGoodbye, мы включаем callback напрямую в вызов функции greet с помощью arrow функции.

Это особенно полезно, когда callback короткий и мы не планируем использовать его в elsewhere в нашем коде.

Callback Hell и как избежать его

По мере роста сложности ваших программ, вы можете发现自己嵌套 callbacks внутри callbacks. Это может привести к ситуации, известной как "callback hell" или "pyramid of doom". Она выглядит примерно так:

asyncOperation1((error1, result1) => {
if (error1) {
handleError(error1);
} else {
asyncOperation2(result1, (error2, result2) => {
if (error2) {
handleError(error2);
} else {
asyncOperation3(result2, (error3, result3) => {
if (error3) {
handleError(error3);
} else {
// И так далее...
}
});
}
});
}
});

Чтобы избежать этого, мы можем использовать такие методы, как:

  1. Именованные функции вместо анонимных функций
  2. Promises
  3. Async/await (который использует promises под капотом)

Вот таблица, резюмирующая эти методы:

Method Description Pros Cons
Named Functions Определять separate функции для каждого callback Улучшает читаемость Может все еще привести к многим вложенным функциям
Promises Использовать .then() chains Упрощает вложенность, лучшее управление ошибками Требует понимания концепции promise
Async/Await Использовать async функции и await ключевые слова Looks like synchronous code, very readable Требует понимания promise и async функций

Заключение

Callbacks - это fundamental concept в Node.js и JavaScript в целом. Они позволяют нам эффективно работать с асинхронными операциями, делая наши программы более эффективными иresponsive. По мере того как вы продолжаете свое путешествие в программировании, вы часто будете сталкиваться с callbacks, и понимание их поможет вам стать более искусным разработчиком.

Помните, как при изучении любой новой навыка, овладение callbacks требует практики. Не отчаивайтесь, если это не сработает сразу - продолжайте программировать, продолжайте экспериментировать, и скоро вы будете использовать callbacks как профи!

Счастливого кодирования, будущие разработчики! И помните, в мире программирования мы не говорим "пока" - мы просто callback позже!

Credits: Image by storyset