Функция обратного вызова - это просто функция, которую вы передаете другой функции, чтобы эта функция могла вызвать ее позже. Это обычно наблюдается в асинхронных 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, который обычно используется под названием обещание. Если вы хотите прочитать о них, вы можете прочитать всю запись в блоге, которую я написал на основе этого ответа здесь .