30

私はしばらくこれに苦労してきました。私は Javascript を初めて使用し、私が書いたコードが非同期で実行されているという印象を受けました。一般的な例を次に示します。

関数 a でいくつかのコードを実行します。次に、関数 A は関数 B を呼び出します。関数 B は、変数を A に返して、A が後の操作で使用できるようにする必要があります。A が B を呼び出すと、戻り値がブロックされるのを待たずに独自のコードを実行し続け、A が戻り値を使用する必要があるポイントに到達するほど B は十分に高速ではないようです。値と未定義の変数型エラーが発生します。

私がこれを回避した方法は、関数Aが関数Bを呼び出し、次に関数Cを呼び出して、Aが戻り値で行う後の操作を実行することです....私は呼び出しを通じてコードをシリアル化しています返品の代わりに...それは面倒ですが...

実際のコードで発生する場合の例を次に示します。

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

注:初期化は、HTML の body onload="initialize()" によって呼び出されます。

したがって、問題は、makeMap が Geocode 関数によって取得された緯度と経度の値を必要とすることですが、結果が未定義であるというエラーがコンソールに表示されます。何が起こっている?私はJavaから来たので、ここでJSでデータフローがどのように起こっているかについて少し混乱しています! 今後の貴重な教訓となります!

副次的な質問:外部スクリプト間で関数を分割するにはどうすればよいですか? グッドプラクティスとは何ですか?すべての関数を 1 つの外部 .js ファイルに詰め込む必要がありますか、それとも同様の関数をグループ化する必要がありますか?

4

4 に答える 4

39

あなたは問題をよく理解しているように見えますが、それを解決する方法に慣れていないようです。これに対処する最も一般的な方法は、コールバックを使用することです。これは基本的に戻り値を待つ非同期の方法です。あなたのケースでそれを使用する方法は次のとおりです。

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

}

...
于 2012-11-19T13:57:10.490 に答える
15

私は...私が書いているコードが非同期で実行されているという印象を受けました。

はい、そうです。関数はGoogle呼び出しが完了する前に戻るため、関数はGoogleAPIへの呼び出しの結果を返すgeocodeことができません。以下の注を参照してください。

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

たとえば、完全に非同期になります。

于 2012-11-19T13:54:22.660 に答える
1

匿名関数内のreturnステートメントは、外部のジオコード関数からではなく、匿名関数から戻ります。geocode関数はundefinedを返します。geocoder.geocodeメソッドは、必要に応じて、同期または非同期で無名関数を呼び出すことができます。ドキュメントを確認してください。

于 2012-11-19T13:54:39.570 に答える
1

実際、呼び出しが非同期であり、適切な戻り値を取得していないことを理解しているのは正しいことです。

通常、関数が js で呼び出されると、関数は同期します。

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

ただし、ajax や 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);
}
于 2012-11-19T13:55:21.317 に答える