2008-11-09 17 views
15

Esto es una especie de pregunta de acertijo, ya que el código funciona perfectamente bien tal como está, simplemente irrita mi sentido estético muy levemente. Estoy recurriendo a Stack Overflow porque mi propio cerebro me está fallando en este momento.Funciones de devolución de llamada de Javascript y recursión

Aquí hay un fragmento de código que busca una dirección usando la API JS de Google Maps y coloca un marcador en un mapa. Sin embargo, a veces la búsqueda inicial falla, por lo que quiero repetir el proceso con una dirección diferente.

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

(El segundo parámetro a getLatLng es una función de devolución de llamada.)

Por supuesto, se puede ver que las tres líneas que se centran y ampliar el mapa y añadir el marcador se duplican, una vez en la devolución de llamada primaria y una vez en la "devolución de llamada alternativa" (ja, ja). ¿Puedes encontrar una manera de expresar todo sin redundancia? Usted gana puntos de bonificación, y mi adulación, si su solución funciona para una cantidad arbitraria de cadenas de mapas de respaldo.

Respuesta

21

Las otras respuestas son buenas, pero esta es una opción más. Esto le permite mantener la misma forma que se inició con, pero utiliza el truco de nombrar a su función lambda para que pueda referirse a ella de forma recursiva:

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

La primera vez que se utiliza el símbolo "lambda", que es introducirlo como un nuevo nombre literal de función. La segunda vez que se usa, es una referencia recursiva.

función de nombres literales funciona en Chrome, y supongo que funciona en la mayoría de los navegadores modernos, pero no lo he probado y no sé acerca de los navegadores más antiguos.

+0

No necesita nombres literales, puede usar lo que utilicé en mi solución - arguments.callee se refiere a la función. –

+4

La nomenclatura literal es mucho más clara y menos hablante que su solución, aunque –

+0

Nombrar la función en lugar de permitir que se refiera a sí misma a través de arguments.callee es "way" cleaner? LOL - Creo que esto es algo subjetivo, para ser honesto. :) –

1

¿Qué tal esto?

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

Cuantas cadenas de respaldo como desee. Simplemente llámalo con un 0 como el segundo argumento la primera vez.

2

Sí, el factor a cabo en una función :)

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

Hay un método muy bueno para realizar la recursividad en las construcciones del lenguaje que no apoyan explícitamente la recursividad llama un combinador punto fijo. El más conocido es el 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); 
    }); 
} 

Esto se ve un poco de miedo, pero sólo tiene que escribir una vez. Usarlo es bastante simple. Básicamente, tomas tu lambda original de un parámetro y la conviertes en una nueva función de dos parámetros: el primer parámetro es ahora la expresión lambda real en la que puedes hacer la llamada recursiva, el segundo parámetro es el primer parámetro original (point) que desea usar.

Así es como podría usarlo en su ejemplo. Tenga en cuenta que estoy usando mapstrings como una lista de cadenas para buscar y que la función pop eliminaría de forma destructiva un elemento del encabezado.

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)); 
    }); 
Cuestiones relacionadas