Javascript callback no arguments

Понимание callback-функций (колбеков)

Callback-функции чрезвычайно важны в языке Javascript. Они есть практически повсюду. Но, несмотря на имеющийся опыт программирования на C/Java, с ними у меня возникли трудности (как и с самой идеей асинхронного программирования), и я решил в этом разобраться. Странно, но я не смог найти в интернете хороших вводных статей о callback-функциях — в основном попадались отрывки документации по функциям call() и apply() или короткие кусочки кода, демонстрирующие их использование, и вот, набив шишек в поисках истины, я решил написать введение в callback-функции самостоятельно.

Функции — это объекты

Чтобы понимать callback-функции, нужно понимать обычные функции. Это может показаться банальностью, но функции в Javascript’е — немного странные штуки.

Функции в Javascript’е — на самом деле объекты. А именно, объекты класса Function , создаваемые конструктором Function . В объекте Function содержится строка с JS-кодом данной функции. Если вы перешли с языка C или Java, это может показаться странным (как код может быть строкой?!), но, вообще говоря, в Javascript’е такое сплошь и рядом. Различие между кодом и данными иногда размывается.

// можно создать функцию, передав в конструктор Function строку с кодом var func_multiply = new Function("arg1", "arg2", "return arg1 * arg2;"); func_multiply(5, 10); // => 50 

Преимущество концепции «функция-как-объект» заключается в том, что код можно передавать в другую функцию точно так же, как обычную переменную или объект (потому что в буквальном понимании код — всего лишь объект).

Передача функции как callback-функции

Передавать функцию в качестве аргумента просто.

// определяем нашу функцию с аргументом callback function some_function(arg1, arg2, callback) < // переменная, генерирующая случайное число в интервале между arg1 и arg2 var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2); // теперь всё готово и мы вызываем callback, куда передаём наш результат callback(my_number); >// вызываем функцию some_function(5, 15, function (num) < // эта анонимная функция выполнится после вызова callback-функции console.log("callback called! " + num); >); 

Может показаться глупостью создавать такой перемудрённый код, когда можно вернуть значение нормальным способом, но существуют ситуации, в которых это непрактично и callback-функции необходимы.

Читайте также:  User admin class php

Не загромождайте выход

Традиционно функции в ходе выполнения принимают на вход аргументы и возвращают значение, используя выражение return (в идеальном случае единственное выражение return в конце функции: одна точка входа и одна точка выхода). Это имеет смысл. Функции — это, в сущности, маршруты между вводом и выводом.

Javascript даёт возможность делать всё немного по-другому. Вместо того чтобы дожидаться, пока функция закончит выполняться и вернёт значение, мы можем использовать callback-функции, чтобы получить его асинхронно. Это полезно для случаев, когда требуется много времени для завершения, например, при AJAX-запросах, ведь мы не можем приостановить браузер. Мы можем продолжить заниматься другими делами в ожидании вызова колбека. Фактически, очень часто от нас требуется (или, точнее, нам настоятельно рекомендуется) делать всё асинхронно в Javascript’е.

Вот более детальный пример, в котором используется AJAX для загрузки XML-файла и используется функция call() для вызова callback-функции в контексте запрошенного объекта (это значит, что когда мы укажем ключевое слово this внутри callback-функции, оно будет ссылаться на запрошенный объект):

function some_function2(url, callback) < var httpRequest; // создаём наш XMLHttpRequest-объект if (window.XMLHttpRequest) < httpRequest = new XMLHttpRequest(); >else if (window.ActiveXObject) < // для дурацкого Internet Explorer'а httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); >httpRequest.onreadystatechange = function () < // встраиваем функцию проверки статуса нашего запроса // это вызывается при каждом изменении статуса if (httpRequest.readyState === 4 && httpRequest.status === 200) < callback.call(httpRequest.responseXML); // вызываем колбек >>; httpRequest.open('GET', url); httpRequest.send(); > // вызываем функцию some_function2("text.xml", function () < console.log(this); >); console.log("это выполнится до вышеуказанного колбека"); 

В этом примере мы создаём объект httpRequest и загружаем файл XML. Типичная парадигма возвращения значения в конце функции тут больше не работает. Наш запрос обрабатывается асинхронно, а это означает, что мы начинаем запрос и говорим ему вызвать нашу функцию, как только он закончится.

Мы используем здесь две анонимных функции. Важно помнить, что нам бы не составило труда использовать и именованные функции, но во имя лаконичности мы сделали их вложенными. Первая анонимная функция выполняется всякий раз при изменении статуса в нашем объекте httpRequest. Мы игнорируем это до тех пор, пока состояние не будет равно 4 (т.е. запрос выполнен) и статус будет равен 200 (т.е. запрос выполнен успешно). В реальном мире вам бы захотелось проверить, не провален ли запрос, но мы предполагаем, что файл существует и может быть загружен браузером. Эта анонимная функция связана с httpRequest.onreadystatechange, так что она выполняется не сразу, а вызывается каждый раз при изменении состояния в нашем запросе.

Когда мы наконец завершаем наш AJAX-запрос, мы не просто запускаем callback-функцию, мы используем для этого функцию call() . Это ещё один способ вызова callback-функции. Метод, использованный нами до этого — простой запуск функции здесь сработал бы хорошо, но я подумал, что стоит продемонстрировать и использование функции call() . Как вариант, можно использовать функцию apply() (обсуждение разницы между ней и call() выходят за рамки этой статьи, скажу лишь, что это затрагивает способ передачи аргументов функции).

В использовании call() замечательно то, что мы сами устанавливаем контекст, в котором выполняется функция. Это означает, что когда мы используем ключевое слово this внутри нашей callback-функции, оно ссылается на то, что мы передаём первым аргументом в call() . В данном примере, когда мы ссылались на this внутри нашей анонимной функции, мы ссылались на responseXML, полученный в результате AJAX-запроса.

Наконец, второе выражение console.log выполнится первым, потому что callback-функция не выполняется до тех пор, пока не закончен запрос, и пока это произойдёт, последующие части кода продолжают спокойно выполняться.

Обёртывай это

Надеюсь, теперь вы стали понимать callback-функции достаточно хорошо, чтобы начать их использовать в своём собственном коде. Мне всё ещё трудно структурировать код, который зиждется на callback-функциях (в конце концов он становится похож на спагетти… мой разум слишком привык к обычному структурному программированию), но они — очень мощный инструмент и одна из интереснейших частей языка Javascript.

Источник

Avoid global variable for callback function called by function with no arguments

My website has multiple pages that use the Google Maps api. Each page uses a different part of the api (e.g. autocomplete, displaying markers etc). However, every page needs to call the initMap() function for the api to be initialized. The logic that calls initMap() is controlled by google and it cannot take parameters. maps.js

function initMap() < const mapOptions = < center: , zoom: 7 >; window.map = new google.maps.Map(document.getElementById("map"), mapOptions); if (typeof callback !== 'undefined') < callback(); // defined above html link to maps.js >> 

Instead of writing out a different initMap() function for every page, I opted to have it in one .js file which is linked to every page. After initializing the map, the function executes a callback function which is defined in each page’s corresponding .js file. index.js

As initMap() cannot take parameters, I had to define the callback function’s name using a cross-script global variable before maps.js is linked. This variable is different on every page depending what function needs to be run in initMap() after the map is initialized.

   

Источник

Оцените статью