5

比較的些細な機能以外の目的でJavascriptを書くことを恐れる傾向がある理由のひとつは、あるものが実際に別のものに依存しているときに、コールバックのウォーターフォールを回避するための適切なアプローチを見つけられなかったためです。そのようなアプローチはありますか?

私は現在Titaniumアプリに取り組んでおり、この現実世界のシナリオにぶつかっています。

ユーザーの現在地からの距離を計算するために必要な一連の施設があります。これには、ユーザーの現在の位置(1回だけ発生する必要があります)を取得し、施設の場所をループしながら、各場所の位置を取得して距離を計算する必要があります。場所(long / lat)を取得するAPIは非同期であるため、「簡単な」アプローチは次のようになります(擬似コードが続きます)。

foreach facility {
  API.getCurrentLocation( function( location ) { // async, takes a callback fxn arg
    var here = location.coordinates;

    API.getFacilityLocation( function( e ) { // async, takes a callback fxn arg
      var there    = e. coordinates;
      var distance = API.calculateFrom( here, there );
    });
  });
}

ただし、これはすべてループ内にあるため、毎回現在の位置を計算しています。実際に必要な作業よりも多くの作業が必要です。現在の場所を1回だけ取得し、その場所を距離計算に使用できるように、これをリファクタリングすることはまだできていません。

ラムダとクロージャをサポートする言語が爆発的に増えていることを考えると、誰かがこれらのウォーターフォールを管理しやすくするためのアプローチを見つけたに違いないと私は考え続けていますが、そのようなソリューションを整理する方法についての適切な説明はまだ見つかりません。

何か提案やヒントはありますか?

どんな洞察も大歓迎です。

4

4 に答える 4

4

基本的な戦略:コールバックに無名関数を使用せず、現在の場所が返されたときに実行されるコールバックにループを移動します。

例:

function recieveCurrentLocation(location) { // async, takes a callback fxn arg
    var here = location.coordinates;

    // Have to define this callback in the inner scope so it can close
    // over the 'here' value
    function recieveFacilityLocation(e) {
        var there    = e. coordinates;
        var distance = API.calculateFrom( here, there );
    }

    foreach facility {
        API.getFacilityLocation(recieveFacilityLocation);
    }
}

API.getCurrentLocation(recieveCurrentLocation);
于 2011-11-15T14:00:11.733 に答える
3

あなたはもっとイベント指向を考え始めなければなりません。各コールバックレベルの関数を定義し、必要に応じて引数として提供します。コールバックのウォーターフォールとは考えないでください。バッチ以外の各プロセスでも同じであることに注意してください。ユーザーアクションを待機し、アクションを実行する大きなイベントループがあり、他のユーザーアクションを待機し、別のイベント処理アクションを実行します。

現時点で実行できることを、非同期レジスタハンドラーに対して実行するだけです。コンピュータシステムからのユーザーアクションと非同期応答はそれほど違いはありません:)

于 2011-11-15T14:59:00.883 に答える
1

ここには2つの別々の問題があります。1つ目は、コールバックを(「ワットフォール」方式で)ネストすることであり、2つ目は、どの継続をパスするかを知らずに非同期関数を呼び出すことです。

地獄の入れ子を避けるための基本的な考え方は、代わりに名前関数を使用することです。それで

f1(arg1, function(){
    arg2 = g(arg1);
    f2(function(){
        ...use arg2
    });
});

になることができる

var arg2;
f1(arg1, afterf1);

function afterf1(){
    arg2 = g(arg1);
    f2(afterf2);
}

function afterf2(){
    ...use arg2;
}

他の唯一の主なリファクタリングは、内部関数が内部関数ではなくなるため、内部関数が外部スコープに閉じられるようにすべての変数を移動する必要があることに注意してください(共有変数を最小限に保つようにしてください-ありますそれらの数が多すぎると感じた場合に、それらをより管理しやすいコードにリファクタリングするための多くのトリック)。

さて、もう1つの問題は、の値をいつ使用するかわからないコールバックがあることです

同期の場合、次のことができます

var x = f();

そして、xが欲しい人は誰でも、後でいつでもそれにアクセスできます。

しかし、非同期の場合、あなたはすることに制限されています

f(function(x){
   ...use x here
});

そして、これまでに見ることができる唯一のコードはx、このコールバックによって制御されます。

秘訣は、後で「実際の」コールバックを追加し、元の関数に渡したコールバックを直接使用するのではなく、結果を関係者に渡す方法を用意することです。

var got_result = false;
var result = null;
var waiting_for_result = [];

function register_callback(f){
    if(got_result){
        f(result);
    }else{
        waiting_for_result.push(f);
    }
}

var real_callback = function(x){
    got_result = true;
    result = x;
    for(var i=0; i< waiting_for_result.length; i++){
        waiting_for_result[i](result);
    }
}

//

API.getCurrentLocation(real_callback);
foreach facility {
    register_callback(function(location){
        ...stuff
    })

もちろん、これを行うことは反復的なPITAであるため、これを正確に行う多くのPromiseライブラリがあります。ほとんどの場合、無名関数を使用してネストされていない「名前付きコールバック」パターンを実行できる適切なメソッドもあります。

たとえば、道場ではこれは次のようになります

var location_promise = API.GetLocationPromise();
foreach facility {
    location_promise.then(function(location){
        ...use location
    });
}
于 2011-11-15T15:40:36.413 に答える
0

ループの外側で現在の場所を定義しないのはなぜですか?

var here ;    
API.getCurrentLocation( function( location ) {here = location.coordinates;})

    foreach facility {

        API.getFacilityLocation( function( e ) { // async, takes a callback fxn arg
          var there    = e. coordinates;
          var distance = API.calculateFrom( here, there );
        });
    }

このようにあなたはそれを一度だけ計算します

于 2011-11-15T13:56:21.380 に答える