Функции обратного вызова Javascript и рекурсия
Это своего рода вопрос, связанный с мозгом, поскольку код работает отлично, как есть, он просто слегка раздражает мое эстетическое чувство. Я обращаюсь к Stack Overflow, потому что мой собственный мозг сейчас не работает.
Вот фрагмент кода, который ищет адрес с помощью API Google Maps JS и помещает маркер на карту. Однако иногда исходный поиск не работает, поэтому я хочу повторить процесс с другим адресом.
geocoder.getLatLng(item.mapstring, function(point) {
if (!point) {
geocoder.getLatLng(item.backup_mapstring, function(point) {
if (!point) return;
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
})
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
})
(Второй параметр getLatLng
- функция обратного вызова.)
Конечно, вы можете видеть, что три линии, которые центрируют и масштабируют карту и добавляют маркер, дублируются, один раз в основном обратном вызове и один раз в "обратном обратном вызове" (ха-ха). Можете ли вы найти способ выразить все это без какой-либо избыточности? Вы зарабатываете бонусные очки и мою лесть, если ваше решение работает для произвольного количества строк резервной карты.
Ответы
Ответ 1
Другие ответы хороши, но вот еще один вариант. Это позволяет сохранить ту же форму, с которой вы начали, но использует трюк для обозначения вашей лямбда-функции, чтобы вы могли ссылаться на нее рекурсивно:
mapstrings = ['mapstring1', 'mapstring2', 'mapstring3'];
geocoder.getLatLng(mapstrings.shift(), function lambda(point) {
if(point) {
// success
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
}
else if(mapstrings.length > 0) {
// Previous mapstring failed... try next mapstring
geocoder.getLatLng(mapstrings.shift(), lambda);
}
else {
// Take special action if no mapstring succeeds?
}
})
В первый раз, когда используется символ "лямбда", он должен вводить его в качестве нового имени функции. Второй раз, когда он используется, это рекурсивная ссылка.
Функция literal именования работает в Chrome, и я предполагаю, что она работает в большинстве современных браузеров, но я ее не тестировал, и я не знаю о старых браузерах.
Ответ 2
Существует чрезвычайно хороший метод для выполнения рекурсии в языковых конструкциях, которые явно не поддерживают рекурсию, называемую комбинатором с фиксированной точкой. Наиболее известным является Y-Combinator.
Вот Y combinator для функции одного параметра в Javascript:
function Y(le, a) {
return function (f) {
return f(f);
}(function (f) {
return le(function (x) {
return f(f)(x);
}, a);
});
}
Это выглядит немного страшно, но вам нужно только написать это один раз. Использование этого на самом деле довольно просто. В принципе, вы берете свою оригинальную лямбда одного параметра, и вы превращаете ее в новую функцию из двух параметров - первый параметр теперь является фактическим лямбда-выражением, которое вы можете выполнить рекурсивным вызовом, вторым параметром является первоначальный первый параметр ( point
), который вы хотите использовать.
Вот как вы можете использовать его в своем примере. Обратите внимание, что я использую mapstrings
как список строк для поиска, а функция pop разрушает элемент из головы.
geocoder.getLatLng(pop(mapstrings), Y(
function(getLatLongCallback, point)
{
if (!point)
{
if (length(mapstrings) > 0)
geocoder.getLatLng(pop(mapstrings), getLatLongCallback);
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
});
Ответ 3
Да, внесите его в функцию:)
geocoder.getLatLng(item.mapstring, function(point) {
if (!point) {
geocoder.getLatLng(item.backup_mapstring, function(point) {
if (point) {
setPoint(point);
}
})
return;
}
function setPoint(point) {
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
}
setPoint(point);
});
Ответ 4
Как насчет этого?
function place_point(mapstrings,idx)
{
if(idx>=mapstrings.length) return;
geocoder.getLatLng(mapstrings[idx],
function(point)
{
if(!point)
{
place_point(mapstrings,idx+1);
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
});
}
Как много резервных строк, как вы хотите. Просто вызовите его с помощью 0 в качестве второго аргумента в первый раз.