понедельник, 17 июля 2017 г.

Что такое функции возврата в JavaScript



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

Что такое функция возврата?

Простой ответ: Функция возврата – это функция которая выполняется когда другая функция (как правило асинхронная) завершило свое выполнение.
Более сложный ответ: В JavaScript функции – это объекты. Поэтому функции могут использовать другие функции в качестве своих аргументов и могут возвращать функции. Такие функции называются функциями верхнего порядка. А функции которые передаются в качестве аргументов и вызываются последовательно функцией которая ее получила называются функциями возврата.
Но это все слова, давайте взглянем на пример.

Зачем нам нужны функции возврата?

По одной очень важной причине – JavaScript – это язык построенный на событиях. Это значит, что вместо того что бы ждать ответа, JavaScript продолжит выполнение и будет прослушивать другие события. Давайте обратимся к примеру.
function first(){
  console.log(1);
}

function second(){
  console.log(2);
}

first();
second();

Как вы можете предположить, функция first выполнится первой, а функция second – второй. Это отражает и вывод на консоли.
// 1
// 2
Пока все хорошо.
Но что если первая функция будет иметь какойто код который не будет выполнятся сиюминутно? Например, запрос API когда нам нужно отослать запрос и дождаться ответа? Это можно симулировать с помощью функции setTimeout, которая является встроеным средством JavaScript и позволяет выполнять некоторый код по прошествии заданного времени. Мы добавим задурждку в 500 мс к нашему коду, что этого что бы сэмулировать запрос API. Наш новый код теперь будет выглядеть так:
function first(){
  // Simulate a code delay
  setTimeout( function(){
    console.log(1);
  }, 500 );
}

function second(){
  console.log(2);
}

first();
second();

Сейчас не важно если вы не совсем понимаете как работает функция setTimeout() (хотя я думаю что поинтересоваться стоит, например почитать здесь). Суть здесь в том, что мы добавили задержку 500 мс к вызову console.log(1);. Итак, что же теперь мы получили?
first();
second();
// 2
// 1
Несмотря на то, что мы вызываем функцию first() первой, в логах результата мы ее видим посде функции second().
Тут дело не в том, что JavaScript не выполняет наши функции в том порядке, в каком мы хотим, а просто JavaScript не ожидает получения ответа от first() и переходит к выполнению second().
Почему я это вам показываю? Потому что вы не можете просто вызвать функции одну за другой и надеятся что они выполнятся именно в том же порядке. Функции возврата предоставляют возможность выполнить быть увереным в том, что ваш код не будет выполнен  до завершения некоторого другого кода.

Создаем функцию возврата

Ладно, хватит болтать, давайте создадим функцию возврата!
Во первых,  откройте консоль разработчика в браузере  (вы можете открыть ее с помощью Ctrl + Shift + J на Windows/Linux или Cmd + Option + J  на Mac). После этого напишите вот такую функцию в консоли:

function doHomework(subject) {
  alert(`Starting my ${subject} homework.`);
}
Выше мы создали функцию doHomework. Она принимает один аргумент – заголовок с которым мы работаем. Вызовите вышу функцию, для этого напечатайте в консоли следующее:
doHomework('math');
// Отобразит сообщение: Starting my math homework.

Теперь давайте ее немного улучшим – в качестве последнего параметра функции doHomework() передадим функцию возврата. Сама функция определена при вызове doHomework() в качестве второго аргумента.
function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}

doHomework('math', function() {
  alert('Finished my homework');
});

Как вы увидите, если вы создадите такой код, то получите два сообщения: вначале сообщение ‘Starting homework’, а затем ‘Finished homework’.
Но функции возврата не всегда получают определение при вызове. Они могут быть объявлены в другом месте, как в этом примере:
function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}

function alertFinished(){
  alert('Finished my homework');
}

doHomework('math', alertFinished);

Результат выполнения этого кода абсолютно такой же как и в предыдущем случае, но код здесь написан немного по другому. Как вы можете видеть мы передали функцию alertFinished в качестве аргумента при вызове doHomework().

Реальный пример

На прошлой неделе я опубликовал статью как создать бот для Твитера  в 38 строках кода. И единственная причина, почему код из той статьи работал – это Twitter API. Когда вы делаете запрос в API, то вы должны подождать ответа прежде чем можете с ним что нибудь делать. Это прекрасный пример использованися функций возврата. Вот примерно так выглядит запрос:
T.get('search/tweets', params, function(err, data, response) {
  if(!err){
    // This is where the magic will happen
  } else {
    console.log(err);
  }
});
 -  T.get просто значит что мы создаем запрос GET для twitter.
 - Три параметра в запросе: search/tweetsопределяет маршрут нашего запроса, params  параметры поиска и третий параметр – анонимная функция которая является функцией возврата.
Функция возврата важна потому что нам нужно подождать ответ с сервера, и только после этого двигаться дальше. Мы не знаем будут ли наши запросы к API успешны или нет, поэтому мы ждем. Как только Twitter ответил, вызывается наша функция. Twitter либо сообщит об ошбике с помощью объекта err (error), или пришлет объект response. В нашей функции мы можем использовать простой if() для того что бы определить был ли запрос успешным, и после этого обрабатывать ответ сообтветсвующим образом.

Вы сделали это

Отличная работа! Теперь вы понимаете что такое функция возврата и как ей пользоваться. Но это лишь маленькая часть айсберга по работе с функциями возврата, еще многому нужно научиться! Если у вас есть вопросы или коментарии – оставляйте их ниже. Я буду рад их услышать.