Функция обратного вызова - это просто функция, которую вы передаете другой функции, чтобы эта функция могла вызвать ее позже. Это обычно наблюдается в асинхронных API ; вызов API возвращается немедленно, потому что он асинхронный, поэтому вы передаете ему функцию, которую API может вызвать, когда он завершит выполнение своей асинхронной задачи.
Самый простой пример, который я могу придумать в JavaScript, - это setTimeout()функция. Это глобальная функция, которая принимает два аргумента. Первый аргумент - это функция обратного вызова, а второй аргумент - задержка в миллисекундах. Функция предназначена для ожидания подходящее количество времени, а затем вызывает функцию обратного вызова.
setTimeout(function () {
console.log("10 seconds later...");
}, 10000);
Возможно, вы видели приведенный выше код раньше, но просто не понимали, что передаваемая вами функция называется функцией обратного вызова. Мы могли бы переписать приведенный выше код, чтобы сделать его более очевидным.
var callback = function () {
console.log("10 seconds later...");
};
setTimeout(callback, 10000);
Обратные вызовы используются в Node повсеместно, потому что Node построен с нуля, чтобы быть асинхронным во всем, что он делает. Даже при разговоре с файловой системой. Вот почему тонна внутренних API-интерфейсов Node принимает функции обратного вызова в качестве аргументов, а не возвращает данные, которые вы можете присвоить переменной. Вместо этого он вызовет вашу функцию обратного вызова, передавая данные, которые вы хотите, в качестве аргумента. Например, вы можете использовать fsбиблиотеку Node для чтения файла. fsМодуль предоставляет два уникальных функций API: readFileи readFileSync.
readFileФункция является асинхронным , а readFileSync, очевидно , нет. Вы можете видеть, что они предполагают, что вы будете использовать асинхронные вызовы, когда это возможно, поскольку они вызвали их, readFileа readFileSyncне readFileи readFileAsync. Вот пример использования обеих функций.
Синхронный:
var data = fs.readFileSync('test.txt');
console.log(data);
Приведенный выше код блокирует выполнение потока до тех пор, пока все содержимое не test.txtбудет считано в память и сохранено в переменной data. В узле это обычно считается плохой практикой. Однако бывают случаи, когда это полезно, например, при написании небольшого небольшого скрипта, чтобы сделать что-то простое, но утомительное, и вы не заботитесь об экономии каждой наносекунды времени, которое вы можете.
Асинхронный (с обратным вызовом):
var callback = function (err, data) {
if (err) return console.error(err);
console.log(data);
};
fs.readFile('test.txt', callback);
Сначала мы создаем функцию обратного вызова, которая принимает два аргумента errи data. Одна из проблем с асинхронными функциями заключается в том, что становится труднее отлавливать ошибки, поэтому многие API-интерфейсы в стиле обратного вызова передают ошибки в качестве первого аргумента функции обратного вызова. Лучше всего проверить, errимеет ли значение значение, прежде чем делать что-либо еще. Если да, остановите выполнение обратного вызова и зарегистрируйте ошибку.
Синхронные вызовы имеют преимущество, когда возникают исключения, потому что вы можете просто поймать их с помощью try/catchблока.
try {
var data = fs.readFileSync('test.txt');
console.log(data);
} catch (err) {
console.error(err);
}
В асинхронных функциях это не работает. Вызов API возвращается немедленно, так что с помощью try/catch. Правильные асинхронные API-интерфейсы, использующие обратные вызовы, всегда отлавливают свои собственные ошибки, а затем передают их в обратный вызов, где вы можете обрабатывать их по своему усмотрению.
Однако, помимо обратных вызовов, существует еще один популярный стиль API, который обычно используется под названием обещание. Если вы хотите прочитать о них, вы можете прочитать всю запись в блоге, которую я написал на основе этого ответа здесь .