2008-11-09 2 views
15

Это своего рода вопрос о головном мозге, поскольку код работает отлично, как есть, он просто немного раздражает мое эстетическое чувство. Я обращаюсь к Stack Overflow, потому что мой собственный мозг терпит неудачу прямо сейчас.Функции обратного вызова Javascript и рекурсия

Вот фрагмент кода, который просматривает адрес с помощью API JS Google Maps и помещает маркер на карту. Однако иногда исходный поиск не работает, поэтому я хочу повторить процесс с другим адресом.

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 является функцией обратного вызова.)

Конечно, вы можете увидеть, что три линии, которые отцентровать и приблизить карту и добавить маркер дублируются, один раз в основной функции обратного вызова и один раз в «обратном обратном вызове» (ха-ха). Можете ли вы найти способ выразить все это без какой-либо избыточности? Вы зарабатываете бонусные баллы и мою лесть, если ваше решение работает для произвольного количества строк резервной карты.

ответ

21

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

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? 
    } 
}) 

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

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

+0

Вам не нужно буквальное имя, вы можете использовать то, что я использовал в своем решении - arguments.callee относится к функции. – 2008-11-09 06:43:54

1

Как насчет этого?

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 в качестве второго аргумента в первый раз.

2

Да, фактор его в функцию :)

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); 
}); 
8

Существует чрезвычайно хороший способ для выполнения рекурсии в языковых конструкциях, которые явно не поддерживают рекурсию называется неподвижной точки комбинатора а. Наиболее известным является Y-Combinator.

Here is the Y combinator for a function of one parameter in 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)); 
    }); 

 Смежные вопросы

  • Нет связанных вопросов^_^