JavaScript: Как работает функция обратного вызова?

#javascript #json #callback

#язык JavaScript

Вопрос:

Я действительно новичок в JS, и у меня много проблем с написанием/пониманием функций обратного вызова, Скажем, например, у меня есть следующий код, но я не хочу

 takeNumbersGreaterThan(5);  

подлежит исполнению до

 insertNumbers();  

закончено

 numbers = [];   greaterThan = [];  insertNumbers(); takeNumbersGreaterThan(5);  insertNumbers(){  for (var i = 0; ilt;11; i  )  {  numbers.push(i)  } }  takeNumbersGreaterThan(number){   for (var m = 0; mlt;numbers.length; m  )  {  if (numbers[m] gt; number)  {  greaterThan.push(numbers[m])  }  }  }  

Как мне это сделать?

Комментарии:

1. Ваш код уже работает таким образом. Нет никаких обратных вызовов.

2. На самом деле вы не используете никаких обратных вызовов! Вы уверены, что хотите знать об обратных вызовах?

3. Программа, которую я пытаюсь создать, намного сложнее, я вызываю такие функции, но вторая всегда терпит неудачу, потому что первая еще не закончена ( первая занимает много времени).

4. Вы не выполняете никаких асинхронных операций в своем коде — обратные вызовы используются для обработки потока этих операций.

5. Ваш основной синтаксис неверен, вы пропускаете function ключевое слово перед определениями функций.

Ответ №1:

Основы (не об обратных вызовах, а о языках программирования)

Чтобы понять обратные вызовы, сначала вы должны понять функции. И чтобы понять функции в javascript, вам сначала нужно понять переменные, значения и функции.

Почти все языки программирования могут работать со значениями. Поэтому, если вы занимались каким-либо программированием, у вас будет базовое представление о том, что такое значения (я собираюсь значительно упростить типы значений здесь и называть как значения, так и ссылки/указатели «значениями»).

Ценность-это вещь. Например, число или строка. Такова 22.31 ценность и "Hello Dave" есть ценность.

В большинстве языков также есть понятие переменных (хотя не во всех). Переменная-это «имя», которое мы даем значениям, чтобы упростить обработку значений. Например, в следующем, x является переменной:

 var x = 12;  

.. и его значение равно 12.

Что позволяют нам делать переменные? Это позволяет нам подставлять значение вместо имени в наших расчетах. Совсем как математика. Например, если x есть 12 , и мы знаем, что можем добавить 1 12 , мы также можем сделать:

 x   1  

Функции-это значения

В javascript функции являются значениями. Например, в следующем примере мы назначаем функцию переменной:

 function a () {return "Hello"} var y = a;  

Поскольку переменные позволяют вам заменять значение именем, то, если вы можете вызвать функцию a , используя синтаксис a() , это означает, что вы также можете сделать это с переменной y :

 y(); // returns "Hello"  

Обратные вызовы

Если функции являются значениями, это также означает, что вы можете передавать функции в качестве аргументов другим функциям. Например, ниже показано, как вы обычно вызываете функцию в другой функции:

 function a () {return "Hello"}  function b () {return a()   " World"}  b(); // returns "Hello World"  

Если вы можете передавать функции в качестве переменной, это означает, что вы можете сделать что-то вроде этого:

 function a () {return "Hello"} function b () {return "Bye"}  function c (functionVariable) {return functionVariable()   " World"}  c(a); // returns "Hello World" c(b); // returns "Bye World"  

Как вы можете видеть. Обратные вызовы вообще не являются чем-то особенным. Они просто являются результатом того факта, что в javascript функции подчиняются тем же правилам, что и другие значения, такие как числа, массивы и строки.

Обратные вызовы не означают асинхронность

Как вы можете видеть из приведенного выше примера, оба вызова функции c возвращают значение. Таким образом, функция c не является асинхронной, даже если она принимает обратный вызов. Таким образом, обратные вызовы могут использоваться как для синхронного, так и для асинхронного кода.

Хорошим примером синхронного обратного вызова является Array.sort() метод. Он сортирует массив, но принимает необязательный обратный вызов для определения способа сортировки (в алфавитном порядке, численно, по фамилии и т.д.).

Почему асинхронному коду нужны обратные вызовы

На данный момент забудьте об ajax или сетевом коде. Давайте вместо этого рассмотрим сценарий, который делает еще более очевидным, почему обратные вызовы используются асинхронным кодом.

Скажем, например, у вас есть кнопка. Теперь, когда пользователь нажимает эту кнопку, вы хотите, чтобы что-то произошло. Как вы это делаете?

Первое, что делает большинство людей, вероятно, что-то вроде этого:

 while (1) {  if (button.isClicked) {  doSomething();  } }  

ОК. Таким образом, это бесконечный цикл, который проверяет только то, нажата ли кнопка, и ничего больше. Тогда как вы ожидаете, что браузер обновит пользовательский интерфейс и будет отслеживать мышь? Это подводит нас к следующему, что люди пытаются сделать:

 while (1) {  if (button.isClicked) {  doSomething();  }  else {  updateUI();  } }  

ОК. Здорово. Но есть две проблемы. Во-первых, если кто-то напишет библиотеку, такую как Google Диаграммы или jQuery, или что-то связанное с пользовательским интерфейсом, они либо напишут свой собственный while(1)... цикл, либо вы должны вручную скопировать/вставить их функцию в свой цикл while. Это не масштабируется. Во-вторых, и это более важно, это неэффективно. Этот цикл while будет использовать 100% процессорного времени для проверки кнопки. Не было бы лучше, если бы браузер мог сообщить нам, когда нажата кнопка.

К счастью, в javascript функции-это просто значения. Вы можете передать функцию браузеру и попросить его выполнить вашу функцию, когда кто-то нажимает кнопку:

 button.addEventListener('click', doSomething);  

Примечание: Обратите внимание на разницу между обработкой функции как переменной и вызовом функции. Если вы хотите рассматривать функцию как переменную, просто используйте ее имя. Если вы хотите вызвать функцию, используйте фигурные скобки, например doSomething() .

Почему все настаивают на написании асинхронных функций

Есть две причины, по которым все, похоже, настаивают на создании асинхронных API, особенно на таких языках, как javascript.

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

Во-вторых, оказывается, что во многих случаях (но, конечно, не во всех) асинхронное однопоточное программирование выполняется так же быстро, а иногда даже быстрее, чем синхронное многопоточное программирование.

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

Ответ №2:

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

Если вы хотите выполнить takeNumbersGreaterThan(5); сразу после insertNumbers(); использования функции обратного вызова, вы можете сделать что-то вроде этого:

 numbers = [];  greaterThan = [];  insertNumbers(takeNumbersGreaterThan, 5);  function insertNumbers(callbackFunction, callbackFunctionParam){  for (var i = 0; ilt;11; i  )  {  numbers.push(i)  }  callbackFunction(callbackFunctionParam); }  function takeNumbersGreaterThan(number){   for (var m = 0; mlt;numbers.length; m  )  {  if (numbers[m] gt; number)  {  greaterThan.push(numbers[m])  }  }  }  

но это всего лишь простой пример того, как вы можете вызвать функцию обратного вызова после некоторого вычисления. Этот код можно было бы улучшить. Дело в том, что вы можете передать свою функцию обратного вызова в качестве параметра своей функции, а затем позже выполнить эту функцию обратного вызова.

Ответ №3:

Вы уже там. Ваш код почти полностью верен. Вы просто пропустили объявление о работе с функциональными ключами.

Приведенный ниже сценарий показывает вам, как запустить takeNumbersGreaterThan после вставки чисел. В моем примере я также изменил знак функции, чтобы передать массив в качестве параметров и избежать некоторых распространенных «ошибок», известных как замыкания.

 var numbers = [];  var greaterThan = [];   var insertNumbers = function(numbers) {  for (var i = 0; ilt;11; i  )  numbers.push(i) }  var takeNumbersGreaterThan = function(number, numbers, greaterThan){   for (var m = 0; mlt;numbers.length; m  ) {  if (numbers[m] gt; number)  greaterThan.push(numbers[m]);  }   }  // run insert numbers insertNumbers(numbers);  // run take numbers greater than takeNumbersGreaterThan(5, numbers, greaterThan);  // log document.write(greaterThan); 

Ответ №4:

Ваш код не использует никаких асинхронных вызовов, поэтому вам не нужно будет использовать какие-либо обратные вызовы для обработки выполнения. Но если вы хотите знать, как это сделать, то это будет именно так.

 numbers = [];  greaterThan = [];   function insertNumbers(callback){  for (var i = 0; ilt;11; i  )  {  numbers.push(i)  }   callback(); // now execute the callback funtion }  function takeNumbersGreaterThan(number){   for (var m = 0; mlt;numbers.length; m  )  {  if (numbers[m] gt; number)  {  greaterThan.push(numbers[m]);  }  }  console.log(greaterThan);  }   insertNumbers(function() { // here we send a functions as argument to insertNumbers which will execute when callback() is called  takeNumbersGreaterThan(5); });  

insertNumbers принимает аргумент под названием «обратный вызов». Когда insertNumbers все закончено, мы просто бежим callback() . При первоначальном вызове insertNumber мы передаем функцию в качестве аргумента, которая будет выполнена, как только insertNumers будет завершена (или callback() вызвана).

Ответ №5:

Код (по большей части) выполняется последовательно. В предоставленном вами коде компьютер выполняет код в том порядке, в котором вы его предоставили. Сначала он создает новый объект массива и устанавливает его в переменную numbers, затем создает новый объект массива и устанавливает его в большую переменную. Затем он запускает функцию insertNumbers. Теперь компьютер переходит к коду, который вы определили в insertNumbers, и выполняет весь этот код. Затем, после того, как он закончит с этим, он вернется к выполнению начального потока кода, в котором он был, который находится в строке 4. Так что теперь он перейдет к коду takeNumbersGreaterThan. Таким образом, функционально вам не нужны никакие обратные вызовы, так как ваш код не делает ничего, что занимает произвольное количество времени.

Это объясняется тем, что вы видите, что takeNumbersGreaterThan не выполняется до тех пор, пока не будут выполнены insertNumbers.

Единственный раз, когда код не выполняется последовательно, — это когда вы начинаете выполнять многоядерный/многопоточный код.

Обратные вызовы используются, когда что-то занимает произвольное количество времени, например, когда вы читаете данные с диска или запрашиваете данные из сети.

Обратные вызовы могут существовать, поскольку функции, определенные в javascript (и многих других языках), существуют в коде как объекты. Если вы не ставите скобки после имени функции, вы фактически ссылаетесь на объект функции, как и на любую другую переменную. Таким образом, вы можете передавать этот объект функции в своем коде и в другие фрагменты кода. Именно это и происходит в данном примере.

 setTimeout(myCallback, 5000)  function myCallback(){  console.log("5 seconds have passed"); }  

Итак, как вы можете видеть, я могу взять свою функцию myCallback и передать ее другой функции, в данном случае setTimeout , для использования после того, как другая функция выполнит задачу.