предисловие
Эта статья была впервые опубликована на платформе общедоступной учетной записи WeChat (itclancoder), если вы хотите получить лучший опыт чтения, вы можете нажать на ссылкуВы действительно понимаете обратные вызовы?
В этой статье вы узнаете, что такое обратный вызов, обратный вызов — это средство асинхронной работы, он повсеместно используется в повседневном использовании, как определить, когда использовать асинхронный (выполнение перехода, ответ позже, отправка запроса, не ожидание возврата, вы можете отправить следующий запрос в любое время, например, заказ еды, получение еды, трансляция, общение с QQ, WeChat и т.д.) или синхронный (выполнение последовательно, чтение кода построчно, что повлияет на последующие код функции, то есть отправка запроса, ожидание возврата, а затем отправка следующего запроса, например, телефонного звонка, вам нужно подождать, пока ваш женский билет ответит, прежде чем вы сможете продолжить следующий сюжет о жестоком обращении с собакой), важность обратного вызова очевидна, но при собеседовании позвольте вам привести примеры, которые В случае асинхронных обратных вызовов кажется, что трудно привести примеры, кроме ответа на Ajax.Эта статья позволит вам узнать различные обратные вызовы.Если текст вводит в заблуждение, вы можете пройти мимо учителей, чтобы дать больше комментариев и исправлений
Начинать
Это самая важная тема для понимания, если вы хотите научиться использовать node. Почти все в узле использует функции обратного вызова. Они не были изобретены узлом, они просто часть языка JavaScript.
Функция обратного вызова — это функция, которая выполняется асинхронно или позднее. Вместо того, чтобы читать код сверху вниз, асинхронная программа может выполнять различные функции в разное время в зависимости от порядка и скорости, с которой выполнялись предыдущие функции (например, HTTP-запросы или чтение файловой системы).
Поскольку определение того, является ли функция асинхронной, различие может сбивать с толку, в зависимости от контекста. Это простой пример синхронизации, что означает, что вы можете читать код сверху вниз, как книгу.
var myNumber = 1
// 声明定义一个功能函数,define the function
function addOne() {
myNumber++;
}
addOne() // 调用函数,run the function
console.log(myNumber) // 2 logs out 2
Код здесь определяет функцию, а затем вызывает эту функцию на следующей строке, ничего не ожидая. Когда функция вызывается, она сразу же увеличивает число на 1, поэтому мы можем ожидать, что после вызова функции число должно быть равно 2. Это ожидание для синхронного кода — он выполняется последовательно от начала до конца.
Однако Node в основном использует асинхронный код. Давайте используем узел для чтения наших чисел из файла с именем number.txt:
var fs = require('fs') // 引入文件
var myNumber = undefined
// 声明一个函数
function addOne() {
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents);
myNumber++;
})
}
addOne(); // 调用函数
console.log(myNumber) // 未定义 - 此行在readFile完成之前运行 logs out undefined日志输出 --这行代码在fs.readfile之前运行,this line gets run before readFile is done
запустите приведенный выше код
Далее следует установить отладку точки останова кода в Node.Почему мы становимся неопределенными, когда распечатываем это время? В этом коде мы используем метод fs.readFile, который является асинхронным методом. Как правило, операции, которые должны взаимодействовать с жестким диском или сетью, будут асинхронными. Если им просто нужно получить доступ к чему-то в памяти или выполнить какую-то работу с процессором, они будут синхронными. Причина в том, что ввод-вывод очень медленный. По приблизительным подсчетам, обмен данными с жестким диском примерно в 100 000 раз медленнее, чем обмен данными с памятью (например, ОЗУ).Когда мы запускаем эту программу, все функции определяются сразу, но не все выполняются сразу. Это основы понимания асинхронного программирования. Когда вызывается addOne, он запускает readFile, а затем переходит к следующему действию, которое он готов сделать. Если делать нечего, узел будет ждать завершения незавершенных операций fs/network, в противном случае он остановится и выйдет из командной строки.
Когда чтение файла завершено (это может занять от нескольких миллисекунд до нескольких секунд или нескольких минут, в зависимости от скорости жесткого диска), он запустит функцию doneReading и выдаст ошибку (если она есть) и содержимое файла
Причина, по которой мы не определили выше, заключается в том, что у нас нет никакой логики в нашем коде, чтобы сообщить оператору console.log подождать, пока оператор readFile не завершится, прежде чем распечатать число
Если вы хотите выполнять какой-то код снова и снова или позже, первый шаг — поместить этот код в функцию. Затем, всякий раз, когда вы хотите запустить свой код, вы можете вызывать эту функцию. это помогает дать вашей функции описательные имена
Обратные вызовы — это просто функции, которые выполняются позже. Ключом к пониманию обратных вызовов является понимание того, что они используются, когда вы не знаете, когда завершится какая-то асинхронная операция, но знаете, где она завершится — последняя строка асинхронной функции! Порядок объявления обратных вызовов сверху вниз не обязательно имеет значение, только логическая/иерархическая вложенность. Сначала разбейте свой код на функции, а затем используйте обратные вызовы, чтобы объявить, зависит ли выполнение одной функции от другой.
Метод fs.readFile предоставляется узлом, является асинхронным и требует много времени для выполнения. Подумайте, что он делает: он должен идти к ОС, которая, в свою очередь, должна обращаться к файловой системе на жестком диске, который может вращаться или не вращаться со скоростью тысячи оборотов в минуту. Затем он должен прочитать данные с помощью головы и отправить их обратно в вашу программу JavaScript через слой. Дайте readFile функцию (называемую функцией обратного вызова), которая будет вызывать ее после извлечения данных из файловой системы. Он помещает полученные данные в переменную JavaScript и вызывает функцию (функцию обратного вызова) с этой переменной. В этом случае переменная называется fileContents, потому что она содержит содержимое читаемого файла.
Рассмотрим пример с рестораном. Во многих ресторанах вам дают номер, который вы ставите на стол, пока ждете свою еду. Это очень похоже на обратные вызовы. Они сообщают серверу, что делать, когда ваш чизбургер готов.
Давайте поместим наш оператор console.log в функцию и передадим его в качестве обратного вызова.
var fs = require('fs')
var myNumber = undefined
function addOne(callback) {
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents)
myNumber++
callback()
})
}
function logMyNumber() {
console.log(myNumber)
}
addOne(logMyNumber)
Теперь функцию logMyNumber можно передать в качестве параметра, который будет переменной обратного вызова внутри функции addOne. После завершения readFile будет вызвана переменная обратного вызова (callback()). Можно вызывать только функции, поэтому, если вы передадите что-либо, кроме функции, это вызовет ошибку.
Когда функция вызывается javascript, код этой функции выполняется немедленно. В этом случае наш оператор журнала будет выполняться, потому что обратный вызов на самом деле является logMyNumber. Помните, только потому, что вы определяете функцию, не означает, что она будет выполнена. Вы должны вызвать функцию для достижения
Чтобы лучше разобрать пример, вот временная шкала событий, которые происходят, когда мы запускаем эту программу.
- Код анализируется, а значит, если есть синтаксические ошибки, они сломают программу. На этом начальном этапе fs и myNumber объявляются как переменные, а addOne и logMyNumber объявляются как функции. Обратите внимание, что это всего лишь объявления. Ни одна функция не вызывается и не вызывается
- Когда выполняется последняя строка нашей программы, вызывается addOne с функцией logMyNumber, передаваемой в качестве параметра обратного вызова. Вызов addOne сначала запустит асинхронную функцию fs.readFile. Эта часть программы займет некоторое время, чтобы завершить
- Поскольку он ожидает завершения readFile, делать нечего, и узел какое-то время простаивает. узел будет доступен для работы, если в это время есть другие дела
- Как только readFile завершается, он выполняет свою функцию обратного вызова doneReading, которая анализирует fileContents на наличие целого числа с именем myNumber, увеличивает значение myNumber, а затем немедленно вызывает функцию, переданную addOne (ее функцию обратного вызова), logMyNumber.
Возможно, самая запутанная часть программирования обратного вызова заключается в том, что функции — это просто объекты, которые можно хранить в переменных и передавать под разными именами. Давать вашим переменным простые и описательные имена важно, чтобы ваш код был читабельным. В общем, в программе node, когда вы видите переменную, такую как callback или cb, вы можете думать о ней как о функции.
Возможно, вы слышали термины «программирование событий» или «цикл событий». Они относятся к тому, как реализован readFile. node сначала отправляет операцию readFile, а затем ждет, пока readFile отправит событие, которое было завершено. Вы можете проверить другие вещи, ожидая узла. Внутри node есть список вещей, которые были отправлены, но еще не сообщены, поэтому node снова и снова проверяет, является ли список полным. Когда это сделано, они выполняют «обработку», например, любые обратные вызовы, которые зависят от их выполнения, называются
Вот версия псевдокода приведенного выше примера.
function addOne(thenRunThisFunction) {
waitAMinuteAsync(function waitedAMinute() {
thenRunThisFunction()
})
}
addOne(function thisGetsRunAfterAddOneFinishes() {})
Представьте, что у вас есть 3 асинхронные функции a, b и c. Каждому из них требуется 1 минута для запуска и вызов функции обратного вызова (переданной в первом аргументе), когда это будет сделано. Если вы хотите указать узлу «запустить a, затем запустить b, когда это будет сделано, затем запустить c», когда b будет выполнено, это будет выглядеть так:
a(function() {
b(function() {
c()
})
})
Когда этот код выполнен, A запускается немедленно, затем через минуту он закончит и позвонит B, то минуту спустя он закончит и вызов C, и, наконец, узел перестанет работать через 3 минуты, потому что нет ничего более Отказ Существуют более элегантные способы написать вышеприведенного примера, но точка в том, что если у вас есть код, который необходимо дождаться другого асинхронного кода, чтобы завершить, то вы можете выразить эту зависимость, поместив код в функции, которые могут быть переданы в качестве обратных вызовов
Дизайн узла требует, чтобы вы учитывали нелинейность. Рассмотрим этот список действий
read a file
process that file
Если бы вы хотели превратить его в псевдокод, вы бы получили это
var file = readFile()
processFile(file)
Этот линейный (пошаговый, последовательный) код не является тем, как работает узел. Если этот код выполняется, оба readFile и processFile будут выполняться одновременно. Это не имеет смысла, потому что readFile займет некоторое время. Вместо этого вам нужно указать, что processFile зависит от readFile для завершения. Это именно то, для чего нужны обратные вызовы! Благодаря тому, как работает JavaScript, вы можете написать эту зависимость разными способами.
var fs = require('fs')
fs.readFile('movie.mp4', finishedReading)
function finishedReading(error, movieData) {
if (error) return console.error(error)
// do something with the movieData
}
Но вы также можете структурировать код следующим образом, и он все равно будет работать:
var fs = require('fs')
function finishedReading(error, movieData) {
if (error) return console.error(error)
// do something with the movieData
}
fs.readFile('movie.mp4', finishedReading)
даже нравится это
var fs = require('fs')
fs.readFile('movie.mp4', function finishedReading(error, movieData) {
if (error) return console.error(error)
// do something with the movieData
})
Суммировать
Обратный вызов часто означает асинхронность, а асинхронность требует времени ожидания, то есть это произойдет в будущем, а не прямо сейчас, она будет выполнена позже, это условное название использования функций JavaScript, часто буквально Некоторые абстракции становятся неуловимыми. вульгарное понимание, это определение функции объявления функций, но она особенная, она должна зависеть от выполнения другой функции, обычно обратные вызовы используются только для ввода-вывода, например, загрузка торрентов, чтение файлов, взаимодействие с базой данных и т. д., соответствующие примеры, привязка событий, делегирование, bind(), addEventListener(), on(), animate(), window.onload и setTimeout() и т. д. Короче говоря, любая функция должна зависеть от обратных вызовов. все выполняются внутри функции. Преимущество ее возврата заключается в эффективном выполнении и одновременном выполнении нескольких задач. Конечно, вы можете больше всего слышать об аде обратных вызовов. Что касается того, как избежать ада обратных вызовов, в следующем разделе будет рассказано вы раскрыли...