2012-11-19 3 views
25

저는 지금 당분간이 문제에 시달렸습니다. 저는 Javascript를 처음 사용하고 있으며 작성한 코드가 비동기 적으로 실행되고 있다는 인상하에있었습니다. 다음은 일반적인 예입니다.JavaScript가 반환 값을 기다리는 것 같지 않습니다.

함수에서 일부 코드를 실행합니다. 함수 A는 함수 B를 호출합니다. 함수 B는 변수를 A로 반환해야하므로 A는 이후 작업에서이 변수를 사용할 수 있습니다. A가 B를 호출 할 때, A는 리턴 값을 기다리지 않고 자신의 코드를 계속 실행하고 B는 반환을 사용하는 데 필요한 지점에 도달하도록 B가 충분히 빠르지는 않습니다. 값이 이고 정의되지 않은 변수 유형 오류이 표시됩니다.

나는이 문제를 해결하기 위해 함수 A 호출 함수 B를 호출하고 반환 함수로 A가 수행 할 함수 C를 호출한다. 나는 일종의 직렬화 대신 반환의 호출을 통해 내 코드는 ... 그 다음은

그것이 실제 코드에서 발생하는 경우의 예입니다 ...하지만 복잡 :

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    var results = geocode(geocoder); 
    makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng()); 

} 

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

function makeMap(lat, long) { 
    // alert(lat); for debuging 
    var mapOptions = { 
     center: new google.maps.LatLng(lat, long), 
     zoom: 17, 
     mapTypeId: google.maps.MapTypeId.ROADMAP 
    }; 
    map = new google.maps.Map(document.getElementById("map_canvas"), 
     mapOptions); 
} 

참고 : 초기화가 몸의 온로드에 의해 호출되는 = "initialize()"내 HTML에.

그래서 문제는 makeMap에 Geocode 함수에서 얻은 위도와 경도 값이 필요하지만 결과가 정의되지 않는다는 오류가 콘솔에 표시된다는 것입니다. 무슨 일 이니? Java에서 왔기 때문에 JS에서 데이터 흐름이 어떻게 발생하는지 조금 혼란스러워합니다! 이것은 미래를위한 귀중한 교훈이 될 것입니다!

측면 질문 : 외부 스크립트에서 내 기능을 어떻게 분할해야합니까? 좋은 연습으로 간주되는 것은 무엇입니까? 모든 기능을 하나의 외부 .js 파일로 묶어 두거나 기능을 함께 그룹화해야합니까?

+3

좋아, 고마워, 전 여기! –

답변

31

당신은 문제의 좋은 이해를 갖고있는 것 같다,하지만 그 당신이 그것을 해결하는 방법에 익숙하지 않은 것처럼 들립니다. 이 문제를 해결하는 가장 일반적인 방법은 콜백을 사용하는 것입니다. 이것은 기본적으로 반환 값을 기다리는 비동기 방식입니다. 귀하의 케이스에서 사용하는 방법은 다음과 같습니다.

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    geocode(geocoder, function(results) { 
     // This function gets called by the geocode function on success 
     makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng());   
    }); 
} 

function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // Call the callback function instead of returning 
      callback(results); 
     } else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

... 
11

... 나는 필자가 작성한 코드가 비동기 적으로 실행되고 있다는 인상하에 있습니다.

예. geocode 함수 은 Google 호출이 완료되기 전에 함수가 반환되기 때문에 호출 결과를 Google API에 반환 할 수 없습니다. 아래 참고 참조 : 그것은 결과를 가지고 때 호출 할 콜백을 허용하도록

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // +---------- This doesn't return anything from your 
      // v   geocode function, it returns a value from the callback 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 
} 

대신에, 당신은 당신의 geocode 기능을 코딩해야합니다. 예컨대 :

// Added a callback arg ---v 
function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // v---------- Call the callback 
      callback(results); 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
      callback(null); // <--- Call the callback with a flag value 
          // saying there was an error 
     } 
    }); 
} 

그럼, 대신에이처럼 사용하는 :

var results = geocode(someArgHere); 
if (results) { 
    doSomething(results); 
} 
else { 
    doSomethingElse(); 
} 

을이 같이 호출 :

geocode(someArgHere, function() { 
    if (results) { 
     doSomething(results); 
    } 
    else { 
     doSomethingElse(); 
    } 
}); 

는 예를 들어, 당신은 완전히 비동기 이동합니다.

+0

그 익명의 기능 (즉, 두 개의 매개 변수 결과, 상태 걸리는) 뭐하는거야? –

+0

@GeorgesKrinker : 예, 호출 코드로 반환하기 전에 결과를 변형 할 필요가 없다면 직접'콜백 '을 전달할 수 있습니다. –

1

익명 함수의 return 문은 외부 지오 코드 함수가 아닌 익명의 함수에서 반환됩니다. 지오 코드 함수는 undefined를 리턴합니다. geocoder.geocode 메소드는 원하는 경우, 동기화 또는 비동기 때마다 익명 함수를 호출 할 수 있습니다. 그것을위한 docs를 검사하십시오.

1

사실, 호출이 비동기이며 올바른 반환 값을 얻지 못한다는 사실을 알고 있습니다.

일반적으로 js에서 함수를 호출하면 동기식입니다.

e.g. a() calls b(), and a() waits until b() to finish before continuing. 

는 그러나, 아약스 또는 JSONP 호출을 같은 특정 상황에서, 그것은 비동기 적으로 수행된다. 이것은 정확히 geocode()에 전화 할 때 일어나는 일입니다.

귀하의 실행 :

initialize() is called; 
initialize() calls geocoder(); 
geocoder makes a request to Google, and returns null in the meantime. 
initialze() calls makemap() 
the Google geocoder returns at some point, and executed the success callback, which you have defined as "return results;", but there is nothing to return, since the function has already ended. 

그래서, 특히, 이미 지오 코더 전화에 내장 된 콜백 활용 :

if (status == google.maps.GeocoderStatus.OK) { 
    makeMap(results); 
} 
+0

외부 서비스 호출 (위의 async google 호출과 같은) 대신 배열을 반복하고 배열을 반환했는데 왜 필요합니까? 왜 그런가요? –

+0

동기되어야합니다. [jsFiddle] (http://jsfiddle.net)에서 이것을 재현 할 수 있습니까? –

+0

그래, 어떻게 사용하는지 모르겠다. http://jsfiddle.net/gpLYH/1/ 문제가 발생하는 곳은 loadData()가 addMarkers()를 호출 할 때 getStations()에서 데이터를 retreave하려고 할 때이다. 방송국과 배열을 반환해야합니다 ... –

관련 문제